All Files (53.91% covered at 11.94 hits/line)
196 files in total.
9685 relevant lines.
5221 lines covered and
4464 lines missed
-
# encoding: utf-8
-
# Encoding.default_internal = 'UTF-8'
-
-
# = CodeRay Library
-
#
-
# CodeRay is a Ruby library for syntax highlighting.
-
#
-
# I try to make CodeRay easy to use and intuitive, but at the same time fully
-
# featured, complete, fast and efficient.
-
#
-
# See README.
-
#
-
# It consists mainly of
-
# * the main engine: CodeRay (Scanners::Scanner, Tokens, Encoders::Encoder)
-
# * the plugin system: PluginHost, Plugin
-
# * the scanners in CodeRay::Scanners
-
# * the encoders in CodeRay::Encoders
-
# * the styles in CodeRay::Styles
-
#
-
# Here's a fancy graphic to light up this gray docu:
-
#
-
# http://cycnus.de/raindark/coderay/scheme.png
-
#
-
# == Documentation
-
#
-
# See CodeRay, Encoders, Scanners, Tokens.
-
#
-
# == Usage
-
#
-
# Remember you need RubyGems to use CodeRay, unless you have it in your load
-
# path. Run Ruby with -rubygems option if required.
-
#
-
# === Highlight Ruby code in a string as html
-
#
-
# require 'coderay'
-
# print CodeRay.scan('puts "Hello, world!"', :ruby).html
-
#
-
# # prints something like this:
-
# puts <span class="s">"Hello, world!"</span>
-
#
-
#
-
# === Highlight C code from a file in a html div
-
#
-
# require 'coderay'
-
# print CodeRay.scan(File.read('ruby.h'), :c).div
-
# print CodeRay.scan_file('ruby.h').html.div
-
#
-
# You can include this div in your page. The used CSS styles can be printed with
-
#
-
# % coderay_stylesheet
-
#
-
# === Highlight without typing too much
-
#
-
# If you are one of the hasty (or lazy, or extremely curious) people, just run this file:
-
#
-
# % ruby -rubygems /path/to/coderay/coderay.rb > example.html
-
#
-
# and look at the file it created in your browser.
-
#
-
# = CodeRay Module
-
#
-
# The CodeRay module provides convenience methods for the engine.
-
#
-
# * The +lang+ and +format+ arguments select Scanner and Encoder to use. These are
-
# simply lower-case symbols, like <tt>:python</tt> or <tt>:html</tt>.
-
# * All methods take an optional hash as last parameter, +options+, that is send to
-
# the Encoder / Scanner.
-
# * Input and language are always sorted in this order: +code+, +lang+.
-
# (This is in alphabetical order, if you need a mnemonic ;)
-
#
-
# You should be able to highlight everything you want just using these methods;
-
# so there is no need to dive into CodeRay's deep class hierarchy.
-
#
-
# The examples in the demo directory demonstrate common cases using this interface.
-
#
-
# = Basic Access Ways
-
#
-
# Read this to get a general view what CodeRay provides.
-
#
-
# == Scanning
-
#
-
# Scanning means analysing an input string, splitting it up into Tokens.
-
# Each Token knows about what type it is: string, comment, class name, etc.
-
#
-
# Each +lang+ (language) has its own Scanner; for example, <tt>:ruby</tt> code is
-
# handled by CodeRay::Scanners::Ruby.
-
#
-
# CodeRay.scan:: Scan a string in a given language into Tokens.
-
# This is the most common method to use.
-
# CodeRay.scan_file:: Scan a file and guess the language using FileType.
-
#
-
# The Tokens object you get from these methods can encode itself; see Tokens.
-
#
-
# == Encoding
-
#
-
# Encoding means compiling Tokens into an output. This can be colored HTML or
-
# LaTeX, a textual statistic or just the number of non-whitespace tokens.
-
#
-
# Each Encoder provides output in a specific +format+, so you select Encoders via
-
# formats like <tt>:html</tt> or <tt>:statistic</tt>.
-
#
-
# CodeRay.encode:: Scan and encode a string in a given language.
-
# CodeRay.encode_tokens:: Encode the given tokens.
-
# CodeRay.encode_file:: Scan a file, guess the language using FileType and encode it.
-
#
-
# == All-in-One Encoding
-
#
-
# CodeRay.encode:: Highlight a string with a given input and output format.
-
#
-
# == Instanciating
-
#
-
# You can use an Encoder instance to highlight multiple inputs. This way, the setup
-
# for this Encoder must only be done once.
-
#
-
# CodeRay.encoder:: Create an Encoder instance with format and options.
-
# CodeRay.scanner:: Create an Scanner instance for lang, with '' as default code.
-
#
-
# To make use of CodeRay.scanner, use CodeRay::Scanner::code=.
-
#
-
# The scanning methods provide more flexibility; we recommend to use these.
-
#
-
# == Reusing Scanners and Encoders
-
#
-
# If you want to re-use scanners and encoders (because that is faster), see
-
# CodeRay::Duo for the most convenient (and recommended) interface.
-
1
module CodeRay
-
-
1
$CODERAY_DEBUG ||= false
-
-
1
CODERAY_PATH = File.expand_path('../coderay', __FILE__)
-
-
# Assuming the path is a subpath of lib/coderay/
-
1
def self.coderay_path *path
-
13
File.join CODERAY_PATH, *path
-
end
-
-
1
require 'coderay/version'
-
-
# helpers
-
1
autoload :FileType, coderay_path('helpers', 'file_type')
-
-
# Tokens
-
1
autoload :Tokens, coderay_path('tokens')
-
1
autoload :TokensProxy, coderay_path('tokens_proxy')
-
1
autoload :TokenKinds, coderay_path('token_kinds')
-
-
# Plugin system
-
1
autoload :PluginHost, coderay_path('helpers', 'plugin')
-
1
autoload :Plugin, coderay_path('helpers', 'plugin')
-
-
# Plugins
-
1
autoload :Scanners, coderay_path('scanner')
-
1
autoload :Encoders, coderay_path('encoder')
-
1
autoload :Styles, coderay_path('style')
-
-
# convenience access and reusable Encoder/Scanner pair
-
1
autoload :Duo, coderay_path('duo')
-
-
1
class << self
-
-
# Scans the given +code+ (a String) with the Scanner for +lang+.
-
#
-
# This is a simple way to use CodeRay. Example:
-
# require 'coderay'
-
# page = CodeRay.scan("puts 'Hello, world!'", :ruby).html
-
#
-
# See also demo/demo_simple.
-
1
def scan code, lang, options = {}, &block
-
94
TokensProxy.new code, lang, options, block
-
end
-
-
# Scans +filename+ (a path to a code file) with the Scanner for +lang+.
-
#
-
# If +lang+ is :auto or omitted, the CodeRay::FileType module is used to
-
# determine it. If it cannot find out what type it is, it uses
-
# CodeRay::Scanners::Text.
-
#
-
# Calls CodeRay.scan.
-
#
-
# Example:
-
# require 'coderay'
-
# page = CodeRay.scan_file('some_c_code.c').html
-
1
def scan_file filename, lang = :auto, options = {}, &block
-
lang = FileType.fetch filename, :text, true if lang == :auto
-
code = File.read filename
-
scan code, lang, options, &block
-
end
-
-
# Encode a string.
-
#
-
# This scans +code+ with the the Scanner for +lang+ and then
-
# encodes it with the Encoder for +format+.
-
# +options+ will be passed to the Encoder.
-
#
-
# See CodeRay::Encoder.encode.
-
1
def encode code, lang, format, options = {}
-
81
encoder(format, options).encode code, lang, options
-
end
-
-
# Encode pre-scanned Tokens.
-
# Use this together with CodeRay.scan:
-
#
-
# require 'coderay'
-
#
-
# # Highlight a short Ruby code example in a HTML span
-
# tokens = CodeRay.scan '1 + 2', :ruby
-
# puts CodeRay.encode_tokens(tokens, :span)
-
#
-
1
def encode_tokens tokens, format, options = {}
-
encoder(format, options).encode_tokens tokens, options
-
end
-
-
# Encodes +filename+ (a path to a code file) with the Scanner for +lang+.
-
#
-
# See CodeRay.scan_file.
-
# Notice that the second argument is the output +format+, not the input language.
-
#
-
# Example:
-
# require 'coderay'
-
# page = CodeRay.encode_file 'some_c_code.c', :html
-
1
def encode_file filename, format, options = {}
-
tokens = scan_file filename, :auto, get_scanner_options(options)
-
encode_tokens tokens, format, options
-
end
-
-
# Highlight a string into a HTML <div>.
-
#
-
# CSS styles use classes, so you have to include a stylesheet
-
# in your output.
-
#
-
# See encode.
-
1
def highlight code, lang, options = { :css => :class }, format = :div
-
encode code, lang, format, options
-
end
-
-
# Highlight a file into a HTML <div>.
-
#
-
# CSS styles use classes, so you have to include a stylesheet
-
# in your output.
-
#
-
# See encode.
-
1
def highlight_file filename, options = { :css => :class }, format = :div
-
encode_file filename, format, options
-
end
-
-
# Finds the Encoder class for +format+ and creates an instance, passing
-
# +options+ to it.
-
#
-
# Example:
-
# require 'coderay'
-
#
-
# stats = CodeRay.encoder(:statistic)
-
# stats.encode("puts 17 + 4\n", :ruby)
-
#
-
# puts '%d out of %d tokens have the kind :integer.' % [
-
# stats.type_stats[:integer].count,
-
# stats.real_token_count
-
# ]
-
# #-> 2 out of 4 tokens have the kind :integer.
-
1
def encoder format, options = {}
-
81
Encoders[format].new options
-
end
-
-
# Finds the Scanner class for +lang+ and creates an instance, passing
-
# +options+ to it.
-
#
-
# See Scanner.new.
-
1
def scanner lang, options = {}, &block
-
13
Scanners[lang].new '', options, &block
-
end
-
-
# Extract the options for the scanner from the +options+ hash.
-
#
-
# Returns an empty Hash if <tt>:scanner_options</tt> is not set.
-
#
-
# This is used if a method like CodeRay.encode has to provide options
-
# for Encoder _and_ scanner.
-
1
def get_scanner_options options
-
81
options.fetch :scanner_options, {}
-
end
-
-
end
-
-
end
-
1
module CodeRay
-
-
# This module holds the Encoder class and its subclasses.
-
# For example, the HTML encoder is named CodeRay::Encoders::HTML
-
# can be found in coderay/encoders/html.
-
#
-
# Encoders also provides methods and constants for the register
-
# mechanism and the [] method that returns the Encoder class
-
# belonging to the given format.
-
1
module Encoders
-
-
1
extend PluginHost
-
1
plugin_path File.dirname(__FILE__), 'encoders'
-
-
# = Encoder
-
#
-
# The Encoder base class. Together with Scanner and
-
# Tokens, it forms the highlighting triad.
-
#
-
# Encoder instances take a Tokens object and do something with it.
-
#
-
# The most common Encoder is surely the HTML encoder
-
# (CodeRay::Encoders::HTML). It highlights the code in a colorful
-
# html page.
-
# If you want the highlighted code in a div or a span instead,
-
# use its subclasses Div and Span.
-
1
class Encoder
-
1
extend Plugin
-
1
plugin_host Encoders
-
-
1
class << self
-
-
# If FILE_EXTENSION isn't defined, this method returns the
-
# downcase class name instead.
-
1
def const_missing sym
-
if sym == :FILE_EXTENSION
-
(defined?(@plugin_id) && @plugin_id || name[/\w+$/].downcase).to_s
-
else
-
super
-
end
-
end
-
-
# The default file extension for output file of this encoder class.
-
1
def file_extension
-
self::FILE_EXTENSION
-
end
-
-
end
-
-
# Subclasses are to store their default options in this constant.
-
1
DEFAULT_OPTIONS = { }
-
-
# The options you gave the Encoder at creating.
-
1
attr_accessor :options, :scanner
-
-
# Creates a new Encoder.
-
# +options+ is saved and used for all encode operations, as long
-
# as you don't overwrite it there by passing additional options.
-
#
-
# Encoder objects provide three encode methods:
-
# - encode simply takes a +code+ string and a +lang+
-
# - encode_tokens expects a +tokens+ object instead
-
#
-
# Each method has an optional +options+ parameter. These are
-
# added to the options you passed at creation.
-
1
def initialize options = {}
-
81
@options = self.class::DEFAULT_OPTIONS.merge options
-
81
@@CODERAY_TOKEN_INTERFACE_DEPRECATION_WARNING_GIVEN = false
-
end
-
-
# Encode a Tokens object.
-
1
def encode_tokens tokens, options = {}
-
options = @options.merge options
-
@scanner = tokens.scanner if tokens.respond_to? :scanner
-
setup options
-
compile tokens, options
-
finish options
-
end
-
-
# Encode the given +code+ using the Scanner for +lang+.
-
1
def encode code, lang, options = {}
-
81
options = @options.merge options
-
81
@scanner = Scanners[lang].new code, CodeRay.get_scanner_options(options).update(:tokens => self)
-
81
setup options
-
81
@scanner.tokenize
-
81
finish options
-
end
-
-
# You can use highlight instead of encode, if that seems
-
# more clear to you.
-
1
alias highlight encode
-
-
# The default file extension for this encoder.
-
1
def file_extension
-
self.class.file_extension
-
end
-
-
1
def << token
-
unless @@CODERAY_TOKEN_INTERFACE_DEPRECATION_WARNING_GIVEN
-
warn 'Using old Tokens#<< interface.'
-
@@CODERAY_TOKEN_INTERFACE_DEPRECATION_WARNING_GIVEN = true
-
end
-
self.token(*token)
-
end
-
-
# Called with +content+ and +kind+ of the currently scanned token.
-
# For simple scanners, it's enougth to implement this method.
-
#
-
# By default, it calls text_token, begin_group, end_group, begin_line,
-
# or end_line, depending on the +content+.
-
1
def token content, kind
-
case content
-
when String
-
text_token content, kind
-
when :begin_group
-
begin_group kind
-
when :end_group
-
end_group kind
-
when :begin_line
-
begin_line kind
-
when :end_line
-
end_line kind
-
else
-
raise ArgumentError, 'Unknown token content type: %p, kind = %p' % [content, kind]
-
end
-
end
-
-
# Called for each text token ([text, kind]), where text is a String.
-
1
def text_token text, kind
-
@out << text
-
end
-
-
# Starts a token group with the given +kind+.
-
1
def begin_group kind
-
end
-
-
# Ends a token group with the given +kind+.
-
1
def end_group kind
-
end
-
-
# Starts a new line token group with the given +kind+.
-
1
def begin_line kind
-
end
-
-
# Ends a new line token group with the given +kind+.
-
1
def end_line kind
-
end
-
-
1
protected
-
-
# Called with merged options before encoding starts.
-
# Sets @out to an empty string.
-
#
-
# See the HTML Encoder for an example of option caching.
-
1
def setup options
-
81
@out = get_output(options)
-
end
-
-
1
def get_output options
-
81
options[:out] || ''
-
end
-
-
# Append data.to_s to the output. Returns the argument.
-
1
def output data
-
@out << data.to_s
-
data
-
end
-
-
# Called with merged options after encoding starts.
-
# The return value is the result of encoding, typically @out.
-
1
def finish options
-
81
@out
-
end
-
-
# Do the encoding.
-
#
-
# The already created +tokens+ object must be used; it must be a
-
# Tokens object.
-
1
def compile tokens, options = {}
-
content = nil
-
for item in tokens
-
if item.is_a? Array
-
raise ArgumentError, 'Two-element array tokens are no longer supported.'
-
end
-
if content
-
token content, item
-
content = nil
-
else
-
content = item
-
end
-
end
-
raise 'odd number list for Tokens' if content
-
end
-
-
1
alias tokens compile
-
1
public :tokens
-
-
end
-
-
end
-
end
-
1
module CodeRay
-
1
module Encoders
-
-
1
map \
-
:loc => :lines_of_code,
-
:plain => :text,
-
:plaintext => :text,
-
:remove_comments => :comment_filter,
-
:stats => :statistic,
-
:term => :terminal,
-
:tty => :terminal,
-
:yml => :yaml
-
-
# No default because Tokens#nonsense should raise NoMethodError.
-
-
end
-
end
-
1
module CodeRay
-
1
module Encoders
-
-
# Outputs code highlighted for a color terminal.
-
#
-
# Note: This encoder is in beta. It currently doesn't use the Styles.
-
#
-
# Alias: +term+
-
#
-
# == Authors & License
-
#
-
# By Rob Aldred (http://robaldred.co.uk)
-
#
-
# Based on idea by Nathan Weizenbaum (http://nex-3.com)
-
#
-
# MIT License (http://www.opensource.org/licenses/mit-license.php)
-
1
class Terminal < Encoder
-
-
1
register_for :terminal
-
-
1
TOKEN_COLORS = {
-
:debug => "\e[1;37;44m",
-
-
:annotation => "\e[34m",
-
:attribute_name => "\e[35m",
-
:attribute_value => "\e[31m",
-
:binary => {
-
:self => "\e[31m",
-
:char => "\e[1;31m",
-
:delimiter => "\e[1;31m",
-
},
-
:char => {
-
:self => "\e[35m",
-
:delimiter => "\e[1;35m"
-
},
-
:class => "\e[1;35;4m",
-
:class_variable => "\e[36m",
-
:color => "\e[32m",
-
:comment => {
-
:self => "\e[1;30m",
-
:char => "\e[37m",
-
:delimiter => "\e[37m",
-
},
-
:constant => "\e[1;34;4m",
-
:decorator => "\e[35m",
-
:definition => "\e[1;33m",
-
:directive => "\e[33m",
-
:docstring => "\e[31m",
-
:doctype => "\e[1;34m",
-
:done => "\e[1;30;2m",
-
:entity => "\e[31m",
-
:error => "\e[1;37;41m",
-
:exception => "\e[1;31m",
-
:float => "\e[1;35m",
-
:function => "\e[1;34m",
-
:global_variable => "\e[1;32m",
-
:hex => "\e[1;36m",
-
:id => "\e[1;34m",
-
:include => "\e[31m",
-
:integer => "\e[1;34m",
-
:imaginary => "\e[1;34m",
-
:important => "\e[1;31m",
-
:key => {
-
:self => "\e[35m",
-
:char => "\e[1;35m",
-
:delimiter => "\e[1;35m",
-
},
-
:keyword => "\e[32m",
-
:label => "\e[1;33m",
-
:local_variable => "\e[33m",
-
:namespace => "\e[1;35m",
-
:octal => "\e[1;34m",
-
:predefined => "\e[36m",
-
:predefined_constant => "\e[1;36m",
-
:predefined_type => "\e[1;32m",
-
:preprocessor => "\e[1;36m",
-
:pseudo_class => "\e[1;34m",
-
:regexp => {
-
:self => "\e[35m",
-
:delimiter => "\e[1;35m",
-
:modifier => "\e[35m",
-
:char => "\e[1;35m",
-
},
-
:reserved => "\e[32m",
-
:shell => {
-
:self => "\e[33m",
-
:char => "\e[1;33m",
-
:delimiter => "\e[1;33m",
-
:escape => "\e[1;33m",
-
},
-
:string => {
-
:self => "\e[31m",
-
:modifier => "\e[1;31m",
-
:char => "\e[1;35m",
-
:delimiter => "\e[1;31m",
-
:escape => "\e[1;31m",
-
},
-
:symbol => {
-
:self => "\e[33m",
-
:delimiter => "\e[1;33m",
-
},
-
:tag => "\e[32m",
-
:type => "\e[1;34m",
-
:value => "\e[36m",
-
:variable => "\e[34m",
-
-
:insert => {
-
:self => "\e[42m",
-
:insert => "\e[1;32;42m",
-
:eyecatcher => "\e[102m",
-
},
-
:delete => {
-
:self => "\e[41m",
-
:delete => "\e[1;31;41m",
-
:eyecatcher => "\e[101m",
-
},
-
:change => {
-
:self => "\e[44m",
-
:change => "\e[37;44m",
-
},
-
:head => {
-
:self => "\e[45m",
-
:filename => "\e[37;45m"
-
},
-
}
-
-
1
TOKEN_COLORS[:keyword] = TOKEN_COLORS[:reserved]
-
1
TOKEN_COLORS[:method] = TOKEN_COLORS[:function]
-
1
TOKEN_COLORS[:escape] = TOKEN_COLORS[:delimiter]
-
-
1
protected
-
-
1
def setup(options)
-
81
super
-
81
@opened = []
-
81
@color_scopes = [TOKEN_COLORS]
-
end
-
-
1
public
-
-
1
def text_token text, kind
-
479
if color = @color_scopes.last[kind]
-
92
color = color[:self] if color.is_a? Hash
-
-
92
@out << color
-
92
@out << (text.index("\n") ? text.gsub("\n", "\e[0m\n" + color) : text)
-
92
@out << "\e[0m"
-
92
if outer_color = @color_scopes.last[:self]
-
40
@out << outer_color
-
end
-
else
-
387
@out << text
-
end
-
end
-
-
1
def begin_group kind
-
56
@opened << kind
-
56
@out << open_token(kind)
-
end
-
1
alias begin_line begin_group
-
-
1
def end_group kind
-
56
if @opened.pop
-
56
@color_scopes.pop
-
56
@out << "\e[0m"
-
56
if outer_color = @color_scopes.last[:self]
-
36
@out << outer_color
-
end
-
end
-
end
-
-
1
def end_line kind
-
@out << (@line_filler ||= "\t" * 100)
-
end_group kind
-
end
-
-
1
private
-
-
1
def open_token kind
-
56
if color = @color_scopes.last[kind]
-
20
if color.is_a? Hash
-
20
@color_scopes << color
-
20
color[:self]
-
else
-
@color_scopes << @color_scopes.last
-
color
-
end
-
else
-
36
@color_scopes << @color_scopes.last
-
36
''
-
end
-
end
-
end
-
end
-
end
-
1
module CodeRay
-
-
# = PluginHost
-
#
-
# A simple subclass/subfolder plugin system.
-
#
-
# Example:
-
# class Generators
-
# extend PluginHost
-
# plugin_path 'app/generators'
-
# end
-
#
-
# class Generator
-
# extend Plugin
-
# PLUGIN_HOST = Generators
-
# end
-
#
-
# class FancyGenerator < Generator
-
# register_for :fancy
-
# end
-
#
-
# Generators[:fancy] #-> FancyGenerator
-
# # or
-
# CodeRay.require_plugin 'Generators/fancy'
-
# # or
-
# Generators::Fancy
-
1
module PluginHost
-
-
# Raised if Encoders::[] fails because:
-
# * a file could not be found
-
# * the requested Plugin is not registered
-
1
PluginNotFound = Class.new LoadError
-
1
HostNotFound = Class.new LoadError
-
-
1
PLUGIN_HOSTS = []
-
1
PLUGIN_HOSTS_BY_ID = {} # dummy hash
-
-
# Loads all plugins using list and load.
-
1
def load_all
-
for plugin in list
-
load plugin
-
end
-
end
-
-
# Returns the Plugin for +id+.
-
#
-
# Example:
-
# yaml_plugin = MyPluginHost[:yaml]
-
1
def [] id, *args, &blk
-
176
plugin = validate_id(id)
-
begin
-
257
plugin = plugin_hash.[](plugin, *args, &blk)
-
176
end while plugin.is_a? String
-
176
plugin
-
end
-
-
1
alias load []
-
-
# Tries to +load+ the missing plugin by translating +const+ to the
-
# underscore form (eg. LinesOfCode becomes lines_of_code).
-
1
def const_missing const
-
1
id = const.to_s.
-
gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
-
gsub(/([a-z\d])([A-Z])/,'\1_\2').
-
downcase
-
1
load id
-
end
-
-
1
class << self
-
-
# Adds the module/class to the PLUGIN_HOSTS list.
-
1
def extended mod
-
2
PLUGIN_HOSTS << mod
-
end
-
-
end
-
-
# The path where the plugins can be found.
-
1
def plugin_path *args
-
6
unless args.empty?
-
2
@plugin_path = File.expand_path File.join(*args)
-
end
-
6
@plugin_path ||= ''
-
end
-
-
# Map a plugin_id to another.
-
#
-
# Usage: Put this in a file plugin_path/_map.rb.
-
#
-
# class MyColorHost < PluginHost
-
# map :navy => :dark_blue,
-
# :maroon => :brown,
-
# :luna => :moon
-
# end
-
1
def map hash
-
2
for from, to in hash
-
23
from = validate_id from
-
23
to = validate_id to
-
23
plugin_hash[from] = to unless plugin_hash.has_key? from
-
end
-
end
-
-
# Define the default plugin to use when no plugin is found
-
# for a given id, or return the default plugin.
-
#
-
# See also map.
-
#
-
# class MyColorHost < PluginHost
-
# map :navy => :dark_blue
-
# default :gray
-
# end
-
#
-
# MyColorHost.default # loads and returns the Gray plugin
-
1
def default id = nil
-
1
if id
-
1
id = validate_id id
-
1
raise "The default plugin can't be named \"default\"." if id == :default
-
1
plugin_hash[:default] = id
-
else
-
load :default
-
end
-
end
-
-
# Every plugin must register itself for +id+ by calling register_for,
-
# which calls this method.
-
#
-
# See Plugin#register_for.
-
1
def register plugin, id
-
2
plugin_hash[validate_id(id)] = plugin
-
end
-
-
# A Hash of plugion_id => Plugin pairs.
-
1
def plugin_hash
-
308
@plugin_hash ||= (@plugin_hash = make_plugin_hash).tap { load_plugin_map }
-
end
-
-
# Returns an array of all .rb files in the plugin path.
-
#
-
# The extension .rb is not included.
-
1
def list
-
Dir[path_to('*')].select do |file|
-
File.basename(file)[/^(?!_)\w+\.rb$/]
-
end.map do |file|
-
File.basename(file, '.rb').to_sym
-
end
-
end
-
-
# Returns an array of all Plugins.
-
#
-
# Note: This loads all plugins using load_all.
-
1
def all_plugins
-
load_all
-
plugin_hash.values.grep(Class)
-
end
-
-
# Loads the map file (see map).
-
#
-
# This is done automatically when plugin_path is called.
-
1
def load_plugin_map
-
2
mapfile = path_to '_map'
-
2
if File.exist? mapfile
-
2
require mapfile
-
2
true
-
else
-
false
-
end
-
end
-
-
1
protected
-
-
# Return a plugin hash that automatically loads plugins.
-
1
def make_plugin_hash
-
2
Hash.new do |h, plugin_id|
-
2
id = validate_id(plugin_id)
-
2
path = path_to id
-
2
begin
-
2
require path
-
rescue LoadError => boom
-
if h.has_key?(:default)
-
h[:default]
-
else
-
raise PluginNotFound, '%p could not load plugin %p: %s' % [self, id, boom]
-
end
-
else
-
# Plugin should have registered by now
-
2
if h.has_key? id
-
2
h[id]
-
else
-
raise PluginNotFound, "No #{self.name} plugin for #{id.inspect} found in #{path}."
-
end
-
end
-
end
-
end
-
-
# Returns the expected path to the plugin file for the given id.
-
1
def path_to plugin_id
-
4
File.join plugin_path, "#{plugin_id}.rb"
-
end
-
-
# Converts +id+ to a valid plugin ID String, or returns +nil+.
-
#
-
# Raises +ArgumentError+ for all other objects, or if the
-
# given String includes non-alphanumeric characters (\W).
-
1
def validate_id id
-
227
case id
-
when Symbol
-
224
id.to_s
-
when String
-
3
if id[/\w+/] == id
-
3
id.downcase
-
else
-
raise ArgumentError, "Invalid id given: #{id}"
-
end
-
else
-
raise ArgumentError, "Symbol or String expected, but #{id.class} given."
-
end
-
end
-
-
end
-
-
-
# = Plugin
-
#
-
# Plugins have to include this module.
-
#
-
# IMPORTANT: Use extend for this module.
-
#
-
# See CodeRay::PluginHost for examples.
-
1
module Plugin
-
-
1
attr_reader :plugin_id
-
-
# Register this class for the given +id+.
-
#
-
# Example:
-
# class MyPlugin < PluginHost::BaseClass
-
# register_for :my_id
-
# ...
-
# end
-
#
-
# See PluginHost.register.
-
1
def register_for id
-
2
@plugin_id = id
-
2
plugin_host.register self, id
-
end
-
-
# Returns the title of the plugin, or sets it to the
-
# optional argument +title+.
-
1
def title title = nil
-
if title
-
@title = title.to_s
-
else
-
@title ||= name[/([^:]+)$/, 1]
-
end
-
end
-
-
# The PluginHost for this Plugin class.
-
1
def plugin_host host = nil
-
4
if host.is_a? PluginHost
-
2
const_set :PLUGIN_HOST, host
-
end
-
4
self::PLUGIN_HOST
-
end
-
-
1
def aliases
-
plugin_host.plugin_hash.inject [] do |aliases, (key, _)|
-
aliases << key if plugin_host[key] == self
-
aliases
-
end
-
end
-
-
end
-
-
end
-
1
module CodeRay
-
-
# = WordList
-
#
-
# <b>A Hash subclass designed for mapping word lists to token types.</b>
-
#
-
# A WordList is a Hash with some additional features.
-
# It is intended to be used for keyword recognition.
-
#
-
# WordList is optimized to be used in Scanners,
-
# typically to decide whether a given ident is a special token.
-
#
-
# For case insensitive words use WordList::CaseIgnoring.
-
#
-
# Example:
-
#
-
# # define word arrays
-
# RESERVED_WORDS = %w[
-
# asm break case continue default do else
-
# ]
-
#
-
# PREDEFINED_TYPES = %w[
-
# int long short char void
-
# ]
-
#
-
# # make a WordList
-
# IDENT_KIND = WordList.new(:ident).
-
# add(RESERVED_WORDS, :reserved).
-
# add(PREDEFINED_TYPES, :predefined_type)
-
#
-
# ...
-
#
-
# def scan_tokens tokens, options
-
# ...
-
#
-
# elsif scan(/[A-Za-z_][A-Za-z_0-9]*/)
-
# # use it
-
# kind = IDENT_KIND[match]
-
# ...
-
1
class WordList < Hash
-
-
# Create a new WordList with +default+ as default value.
-
1
def initialize default = false
-
3
super default
-
end
-
-
# Add words to the list and associate them with +value+.
-
#
-
# Returns +self+, so you can concat add calls.
-
1
def add words, value = true
-
99
words.each { |word| self[word] = value }
-
7
self
-
end
-
-
end
-
-
-
# A CaseIgnoring WordList is like a WordList, only that
-
# keys are compared case-insensitively (normalizing keys using +downcase+).
-
1
class WordList::CaseIgnoring < WordList
-
-
1
def [] key
-
super key.downcase
-
end
-
-
1
def []= key, value
-
super key.downcase, value
-
end
-
-
end
-
-
end
-
# encoding: utf-8
-
1
require 'strscan'
-
-
1
module CodeRay
-
-
1
autoload :WordList, coderay_path('helpers', 'word_list')
-
-
# = Scanners
-
#
-
# This module holds the Scanner class and its subclasses.
-
# For example, the Ruby scanner is named CodeRay::Scanners::Ruby
-
# can be found in coderay/scanners/ruby.
-
#
-
# Scanner also provides methods and constants for the register
-
# mechanism and the [] method that returns the Scanner class
-
# belonging to the given lang.
-
#
-
# See PluginHost.
-
1
module Scanners
-
1
extend PluginHost
-
1
plugin_path File.dirname(__FILE__), 'scanners'
-
-
-
# = Scanner
-
#
-
# The base class for all Scanners.
-
#
-
# It is a subclass of Ruby's great +StringScanner+, which
-
# makes it easy to access the scanning methods inside.
-
#
-
# It is also +Enumerable+, so you can use it like an Array of
-
# Tokens:
-
#
-
# require 'coderay'
-
#
-
# c_scanner = CodeRay::Scanners[:c].new "if (*p == '{') nest++;"
-
#
-
# for text, kind in c_scanner
-
# puts text if kind == :operator
-
# end
-
#
-
# # prints: (*==)++;
-
#
-
# OK, this is a very simple example :)
-
# You can also use +map+, +any?+, +find+ and even +sort_by+,
-
# if you want.
-
1
class Scanner < StringScanner
-
-
1
extend Plugin
-
1
plugin_host Scanners
-
-
# Raised if a Scanner fails while scanning
-
1
ScanError = Class.new StandardError
-
-
# The default options for all scanner classes.
-
#
-
# Define @default_options for subclasses.
-
1
DEFAULT_OPTIONS = { }
-
-
1
KINDS_NOT_LOC = [:comment, :doctype, :docstring]
-
-
1
attr_accessor :state
-
-
1
class << self
-
-
# Normalizes the given code into a string with UNIX newlines, in the
-
# scanner's internal encoding, with invalid and undefined charachters
-
# replaced by placeholders. Always returns a new object.
-
1
def normalize code
-
# original = code
-
120
code = code.to_s unless code.is_a? ::String
-
120
return code if code.empty?
-
-
98
if code.respond_to? :encoding
-
98
code = encode_with_encoding code, self.encoding
-
else
-
code = to_unix code
-
end
-
# code = code.dup if code.eql? original
-
98
code
-
end
-
-
# The typical filename suffix for this scanner's language.
-
1
def file_extension extension = lang
-
1
@file_extension ||= extension.to_s
-
end
-
-
# The encoding used internally by this scanner.
-
1
def encoding name = 'UTF-8'
-
98
@encoding ||= defined?(Encoding.find) && Encoding.find(name)
-
end
-
-
# The lang of this Scanner class, which is equal to its Plugin ID.
-
1
def lang
-
@plugin_id
-
end
-
-
1
protected
-
-
1
def encode_with_encoding code, target_encoding
-
98
if code.encoding == target_encoding
-
58
if code.valid_encoding?
-
58
return to_unix(code)
-
else
-
source_encoding = guess_encoding code
-
end
-
else
-
40
source_encoding = code.encoding
-
end
-
# print "encode_with_encoding from #{source_encoding} to #{target_encoding}"
-
40
code.encode target_encoding, source_encoding, :universal_newline => true, :undef => :replace, :invalid => :replace
-
end
-
-
1
def to_unix code
-
58
code.index(?\r) ? code.gsub(/\r\n?/, "\n") : code
-
end
-
-
1
def guess_encoding s
-
#:nocov:
-
skipped
IO.popen("file -b --mime -", "w+") do |file|
-
skipped
file.write s[0, 1024]
-
skipped
file.close_write
-
skipped
begin
-
skipped
Encoding.find file.gets[/charset=([-\w]+)/, 1]
-
skipped
rescue ArgumentError
-
skipped
Encoding::BINARY
-
skipped
end
-
skipped
end
-
#:nocov:
-
end
-
-
end
-
-
# Create a new Scanner.
-
#
-
# * +code+ is the input String and is handled by the superclass
-
# StringScanner.
-
# * +options+ is a Hash with Symbols as keys.
-
# It is merged with the default options of the class (you can
-
# overwrite default options here.)
-
#
-
# Else, a Tokens object is used.
-
1
def initialize code = '', options = {}
-
94
if self.class == Scanner
-
raise NotImplementedError, "I am only the basic Scanner class. I can't scan anything. :( Use my subclasses."
-
end
-
-
94
@options = self.class::DEFAULT_OPTIONS.merge options
-
-
94
super self.class.normalize(code)
-
-
94
@tokens = options[:tokens] || Tokens.new
-
94
@tokens.scanner = self if @tokens.respond_to? :scanner=
-
-
94
setup
-
end
-
-
# Sets back the scanner. Subclasses should redefine the reset_instance
-
# method instead of this one.
-
1
def reset
-
81
super
-
81
reset_instance
-
end
-
-
# Set a new string to be scanned.
-
1
def string= code
-
13
code = self.class.normalize(code)
-
13
super code
-
13
reset_instance
-
end
-
-
# the Plugin ID for this scanner
-
1
def lang
-
self.class.lang
-
end
-
-
# the default file extension for this scanner
-
1
def file_extension
-
self.class.file_extension
-
end
-
-
# Scan the code and returns all tokens in a Tokens object.
-
1
def tokenize source = nil, options = {}
-
94
options = @options.merge(options)
-
-
94
set_tokens_from_options options
-
94
set_string_from_source source
-
-
94
begin
-
94
scan_tokens @tokens, options
-
rescue => e
-
message = "Error in %s#scan_tokens, initial state was: %p" % [self.class, defined?(state) && state]
-
raise_inspect e.message, @tokens, message, 30, e.backtrace
-
end
-
-
94
@cached_tokens = @tokens
-
94
if source.is_a? Array
-
@tokens.split_into_parts(*source.map { |part| part.size })
-
else
-
94
@tokens
-
end
-
end
-
-
# Cache the result of tokenize.
-
1
def tokens
-
@cached_tokens ||= tokenize
-
end
-
-
# Traverse the tokens.
-
1
def each &block
-
tokens.each(&block)
-
end
-
1
include Enumerable
-
-
# The current line position of the scanner, starting with 1.
-
# See also: #column.
-
#
-
# Beware, this is implemented inefficiently. It should be used
-
# for debugging only.
-
1
def line pos = self.pos
-
return 1 if pos <= 0
-
binary_string[0...pos].count("\n") + 1
-
end
-
-
# The current column position of the scanner, starting with 1.
-
# See also: #line.
-
1
def column pos = self.pos
-
return 1 if pos <= 0
-
pos - (binary_string.rindex(?\n, pos - 1) || -1)
-
end
-
-
# The string in binary encoding.
-
#
-
# To be used with #pos, which is the index of the byte the scanner
-
# will scan next.
-
1
def binary_string
-
@binary_string ||=
-
if string.respond_to?(:bytesize) && string.bytesize != string.size
-
#:nocov:
-
skipped
string.dup.force_encoding('binary')
-
#:nocov:
-
else
-
string
-
end
-
end
-
-
1
protected
-
-
# Can be implemented by subclasses to do some initialization
-
# that has to be done once per instance.
-
#
-
# Use reset for initialization that has to be done once per
-
# scan.
-
1
def setup # :doc:
-
end
-
-
1
def set_string_from_source source
-
94
case source
-
when Array
-
self.string = self.class.normalize(source.join)
-
when nil
-
81
reset
-
else
-
13
self.string = self.class.normalize(source)
-
end
-
end
-
-
1
def set_tokens_from_options options
-
94
@tokens = options[:tokens] || @tokens || Tokens.new
-
94
@tokens.scanner = self if @tokens.respond_to? :scanner=
-
end
-
-
# This is the central method, and commonly the only one a
-
# subclass implements.
-
#
-
# Subclasses must implement this method; it must return +tokens+
-
# and must only use Tokens#<< for storing scanned tokens!
-
1
def scan_tokens tokens, options # :doc:
-
raise NotImplementedError, "#{self.class}#scan_tokens not implemented."
-
end
-
-
# Resets the scanner.
-
1
def reset_instance
-
94
@tokens.clear if @tokens.respond_to?(:clear) && !@options[:keep_tokens]
-
94
@cached_tokens = nil
-
94
@binary_string = nil if defined? @binary_string
-
end
-
-
1
SCAN_ERROR_MESSAGE = <<-MESSAGE
-
-
-
***ERROR in %s: %s (after %s tokens)
-
-
tokens:
-
%s
-
-
%s
-
-
surrounding code:
-
%p ~~ %p
-
-
-
***ERROR***
-
-
MESSAGE
-
-
1
def raise_inspect_arguments message, tokens, state, ambit
-
return File.basename(caller[0]),
-
message,
-
tokens_size(tokens),
-
tokens_last(tokens, 10).map(&:inspect).join("\n"),
-
scanner_state_info(state),
-
binary_string[pos - ambit, ambit],
-
binary_string[pos, ambit]
-
end
-
-
1
SCANNER_STATE_INFO = <<-INFO
-
current line: %d column: %d pos: %d
-
matched: %p state: %p
-
bol?: %p, eos?: %p
-
INFO
-
-
1
def scanner_state_info state
-
SCANNER_STATE_INFO % [
-
line, column, pos,
-
matched, state || 'No state given!',
-
bol?, eos?,
-
]
-
end
-
-
# Scanner error with additional status information
-
1
def raise_inspect message, tokens, state = self.state, ambit = 30, backtrace = caller
-
raise ScanError, SCAN_ERROR_MESSAGE % raise_inspect_arguments(message, tokens, state, ambit), backtrace
-
end
-
-
1
def tokens_size tokens
-
tokens.size if tokens.respond_to?(:size)
-
end
-
-
1
def tokens_last tokens, n
-
tokens.respond_to?(:last) ? tokens.last(n) : []
-
end
-
-
# Shorthand for scan_until(/\z/).
-
# This method also avoids a JRuby 1.9 mode bug.
-
1
def scan_rest
-
rest = self.rest
-
terminate
-
rest
-
end
-
-
end
-
-
end
-
end
-
1
module CodeRay
-
1
module Scanners
-
-
1
map \
-
:'c++' => :cpp,
-
:cplusplus => :cpp,
-
:ecmascript => :java_script,
-
:ecma_script => :java_script,
-
:rhtml => :erb,
-
:eruby => :erb,
-
:irb => :ruby,
-
:javascript => :java_script,
-
:js => :java_script,
-
:pascal => :delphi,
-
:patch => :diff,
-
:plain => :text,
-
:plaintext => :text,
-
:xhtml => :html,
-
:yml => :yaml
-
-
1
default :text
-
-
end
-
end
-
1
module CodeRay
-
1
module Scanners
-
-
# This scanner is really complex, since Ruby _is_ a complex language!
-
#
-
# It tries to highlight 100% of all common code,
-
# and 90% of strange codes.
-
#
-
# It is optimized for HTML highlighting, and is not very useful for
-
# parsing or pretty printing.
-
1
class Ruby < Scanner
-
-
1
register_for :ruby
-
1
file_extension 'rb'
-
-
1
autoload :Patterns, CodeRay.coderay_path('scanners', 'ruby', 'patterns')
-
1
autoload :StringState, CodeRay.coderay_path('scanners', 'ruby', 'string_state')
-
-
1
def interpreted_string_state
-
StringState.new :string, true, '"'
-
end
-
-
1
protected
-
-
1
def setup
-
94
@state = :initial
-
end
-
-
1
def scan_tokens encoder, options
-
94
state, heredocs = options[:state] || @state
-
94
heredocs = heredocs.dup if heredocs.is_a?(Array)
-
-
94
if state && state.instance_of?(StringState)
-
encoder.begin_group state.type
-
end
-
-
94
last_state = nil
-
-
94
method_call_expected = false
-
94
value_expected = true
-
-
94
inline_block_stack = nil
-
94
inline_block_curly_depth = 0
-
-
94
if heredocs
-
state = heredocs.shift
-
encoder.begin_group state.type
-
heredocs = nil if heredocs.empty?
-
end
-
-
# def_object_stack = nil
-
# def_object_paren_depth = 0
-
-
94
patterns = Patterns # avoid constant lookup
-
-
94
unicode = string.respond_to?(:encoding) && string.encoding.name == 'UTF-8'
-
-
94
until eos?
-
-
517
if state.instance_of? ::Symbol
-
-
457
if match = scan(/[ \t\f\v]+/)
-
83
encoder.text_token match, :space
-
-
elsif match = scan(/\n/)
-
if heredocs
-
unscan # heredoc scanning needs \n at start
-
state = heredocs.shift
-
encoder.begin_group state.type
-
heredocs = nil if heredocs.empty?
-
else
-
state = :initial if state == :undef_comma_expected
-
encoder.text_token match, :space
-
value_expected = true
-
end
-
-
elsif match = scan(bol? ? / \#(!)?.* | #{patterns::RUBYDOC_OR_DATA} /ox : /\#.*/)
-
encoder.text_token match, self[1] ? :doctype : :comment
-
-
elsif match = scan(/\\\n/)
-
if heredocs
-
unscan # heredoc scanning needs \n at start
-
encoder.text_token scan(/\\/), :space
-
state = heredocs.shift
-
encoder.begin_group state.type
-
heredocs = nil if heredocs.empty?
-
else
-
encoder.text_token match, :space
-
end
-
-
elsif state == :initial
-
-
# IDENTS #
-
if !method_call_expected &&
-
match = scan(unicode ? /#{patterns::METHOD_NAME}/uo :
-
369
/#{patterns::METHOD_NAME}/o)
-
-
98
kind = patterns::IDENT_KIND[match]
-
98
if value_expected != :colon_expected && scan(/:(?!:)/)
-
value_expected = true
-
encoder.text_token match, :key
-
encoder.text_token ':', :operator
-
else
-
98
value_expected = false
-
98
if kind == :ident
-
88
if match[/\A[A-Z]/] && !(match[/[!?]$/] || match?(/\(/))
-
kind = :constant
-
end
-
elsif kind == :keyword
-
10
state = patterns::KEYWORD_NEW_STATE[match]
-
10
if patterns::KEYWORDS_EXPECTING_VALUE[match]
-
5
value_expected = match == 'when' ? :colon_expected : true
-
end
-
end
-
98
value_expected = true if !value_expected && check(/#{patterns::VALUE_FOLLOWS}/o)
-
98
encoder.text_token match, kind
-
end
-
-
elsif method_call_expected &&
-
match = scan(unicode ? /#{patterns::METHOD_AFTER_DOT}/uo :
-
/#{patterns::METHOD_AFTER_DOT}/o)
-
5
if method_call_expected == '::' && match[/\A[A-Z]/] && !match?(/\(/)
-
encoder.text_token match, :constant
-
else
-
5
encoder.text_token match, :ident
-
end
-
5
method_call_expected = false
-
5
value_expected = check(/#{patterns::VALUE_FOLLOWS}/o)
-
-
# OPERATORS #
-
elsif !method_call_expected && match = scan(/ (\.(?!\.)|::) | ( \.\.\.? | ==?=? | [,\(\[\{] ) | [\)\]\}] /x)
-
156
method_call_expected = self[1]
-
156
value_expected = !method_call_expected && !!self[2]
-
156
if inline_block_stack
-
135
case match
-
when '{'
-
inline_block_curly_depth += 1
-
when '}'
-
45
inline_block_curly_depth -= 1
-
45
if inline_block_curly_depth == 0 # closing brace of inline block reached
-
45
state, inline_block_curly_depth, heredocs = inline_block_stack.pop
-
45
inline_block_stack = nil if inline_block_stack.empty?
-
45
heredocs = nil if heredocs && heredocs.empty?
-
45
encoder.text_token match, :inline_delimiter
-
45
encoder.end_group :inline
-
45
next
-
end
-
end
-
end
-
111
encoder.text_token match, :operator
-
-
elsif match = scan(unicode ? /#{patterns::SYMBOL}/uo :
-
/#{patterns::SYMBOL}/o)
-
1
case delim = match[1]
-
when ?', ?"
-
encoder.begin_group :symbol
-
encoder.text_token ':', :symbol
-
match = delim.chr
-
encoder.text_token match, :delimiter
-
state = self.class::StringState.new :symbol, delim == ?", match
-
else
-
1
encoder.text_token match, :symbol
-
1
value_expected = false
-
end
-
-
elsif match = scan(/ ' (?:(?>[^'\\]*) ')? | " (?:(?>[^"\\\#]*) ")? /mx)
-
25
encoder.begin_group :string
-
25
if match.size == 1
-
15
encoder.text_token match, :delimiter
-
15
state = self.class::StringState.new :string, match == '"', match # important for streaming
-
else
-
10
encoder.text_token match[0,1], :delimiter
-
10
encoder.text_token match[1..-2], :content if match.size > 2
-
10
encoder.text_token match[-1,1], :delimiter
-
10
encoder.end_group :string
-
10
value_expected = false
-
end
-
-
elsif match = scan(unicode ? /#{patterns::INSTANCE_VARIABLE}/uo :
-
/#{patterns::INSTANCE_VARIABLE}/o)
-
value_expected = false
-
encoder.text_token match, :instance_variable
-
-
elsif value_expected && match = scan(/\//)
-
encoder.begin_group :regexp
-
encoder.text_token match, :delimiter
-
state = self.class::StringState.new :regexp, true, '/'
-
-
elsif match = scan(value_expected ? /[-+]?#{patterns::NUMERIC}/o : /#{patterns::NUMERIC}/o)
-
84
if method_call_expected
-
encoder.text_token match, :error
-
method_call_expected = false
-
else
-
84
encoder.text_token match, self[1] ? :float : :integer # TODO: send :hex/:octal/:binary
-
end
-
84
value_expected = false
-
-
elsif match = scan(/ [-+!~^\/]=? | [:;] | [*|&]{1,2}=? | >>? /x)
-
value_expected = true
-
encoder.text_token match, :operator
-
-
elsif value_expected && match = scan(/#{patterns::HEREDOC_OPEN}/o)
-
quote = self[3]
-
delim = self[quote ? 4 : 2]
-
kind = patterns::QUOTE_TO_TYPE[quote]
-
encoder.begin_group kind
-
encoder.text_token match, :delimiter
-
encoder.end_group kind
-
heredocs ||= [] # create heredocs if empty
-
heredocs << self.class::StringState.new(kind, quote != "'", delim,
-
self[1] == '-' ? :indented : :linestart)
-
value_expected = false
-
-
elsif value_expected && match = scan(/#{patterns::FANCY_STRING_START}/o)
-
kind = patterns::FANCY_STRING_KIND[self[1]]
-
encoder.begin_group kind
-
state = self.class::StringState.new kind, patterns::FANCY_STRING_INTERPRETED[self[1]], self[2]
-
encoder.text_token match, :delimiter
-
-
elsif value_expected && match = scan(/#{patterns::CHARACTER}/o)
-
value_expected = false
-
encoder.text_token match, :integer
-
-
elsif match = scan(/ %=? | <(?:<|=>?)? | \? /x)
-
value_expected = match == '?' ? :colon_expected : true
-
encoder.text_token match, :operator
-
-
elsif match = scan(/`/)
-
encoder.begin_group :shell
-
encoder.text_token match, :delimiter
-
state = self.class::StringState.new :shell, true, match
-
-
elsif match = scan(unicode ? /#{patterns::GLOBAL_VARIABLE}/uo :
-
/#{patterns::GLOBAL_VARIABLE}/o)
-
encoder.text_token match, :global_variable
-
value_expected = false
-
-
elsif match = scan(unicode ? /#{patterns::CLASS_VARIABLE}/uo :
-
/#{patterns::CLASS_VARIABLE}/o)
-
encoder.text_token match, :class_variable
-
value_expected = false
-
-
elsif match = scan(/\\\z/)
-
encoder.text_token match, :space
-
-
else
-
if method_call_expected
-
method_call_expected = false
-
next
-
end
-
unless unicode
-
# check for unicode
-
$DEBUG_BEFORE, $DEBUG = $DEBUG, false
-
begin
-
if check(/./mu).size > 1
-
# seems like we should try again with unicode
-
unicode = true
-
end
-
rescue
-
# bad unicode char; use getch
-
ensure
-
$DEBUG = $DEBUG_BEFORE
-
end
-
next if unicode
-
end
-
-
encoder.text_token getch, :error
-
-
end
-
-
324
if last_state
-
state = last_state unless state.is_a?(StringState) # otherwise, a simple 'def"' results in unclosed tokens
-
last_state = nil
-
end
-
-
elsif state == :def_expected
-
5
if match = scan(unicode ? /(?>#{patterns::METHOD_NAME_EX})(?!\.|::)/uo :
-
/(?>#{patterns::METHOD_NAME_EX})(?!\.|::)/o)
-
5
encoder.text_token match, :method
-
5
state = :initial
-
else
-
last_state = :dot_expected
-
state = :initial
-
end
-
-
elsif state == :dot_expected
-
if match = scan(/\.|::/)
-
# invalid definition
-
state = :def_expected
-
encoder.text_token match, :operator
-
else
-
state = :initial
-
end
-
-
elsif state == :module_expected
-
if match = scan(/<</)
-
encoder.text_token match, :operator
-
else
-
state = :initial
-
if match = scan(unicode ? / (?:#{patterns::IDENT}::)* #{patterns::IDENT} /oux :
-
/ (?:#{patterns::IDENT}::)* #{patterns::IDENT} /ox)
-
encoder.text_token match, :class
-
end
-
end
-
-
elsif state == :undef_expected
-
state = :undef_comma_expected
-
if match = scan(unicode ? /(?>#{patterns::METHOD_NAME_EX})(?!\.|::)/uo :
-
/(?>#{patterns::METHOD_NAME_EX})(?!\.|::)/o)
-
encoder.text_token match, :method
-
elsif match = scan(/#{patterns::SYMBOL}/o)
-
case delim = match[1]
-
when ?', ?"
-
encoder.begin_group :symbol
-
encoder.text_token ':', :symbol
-
match = delim.chr
-
encoder.text_token match, :delimiter
-
state = self.class::StringState.new :symbol, delim == ?", match
-
state.next_state = :undef_comma_expected
-
else
-
encoder.text_token match, :symbol
-
end
-
else
-
state = :initial
-
end
-
-
elsif state == :undef_comma_expected
-
if match = scan(/,/)
-
encoder.text_token match, :operator
-
state = :undef_expected
-
else
-
state = :initial
-
end
-
-
elsif state == :alias_expected
-
match = scan(unicode ? /(#{patterns::METHOD_NAME_OR_SYMBOL})([ \t]+)(#{patterns::METHOD_NAME_OR_SYMBOL})/uo :
-
/(#{patterns::METHOD_NAME_OR_SYMBOL})([ \t]+)(#{patterns::METHOD_NAME_OR_SYMBOL})/o)
-
-
if match
-
encoder.text_token self[1], (self[1][0] == ?: ? :symbol : :method)
-
encoder.text_token self[2], :space
-
encoder.text_token self[3], (self[3][0] == ?: ? :symbol : :method)
-
end
-
state = :initial
-
-
else
-
#:nocov:
-
skipped
raise_inspect 'Unknown state: %p' % [state], encoder
-
#:nocov:
-
end
-
-
else # StringState
-
-
60
match = scan_until(state.pattern) || scan_rest
-
60
unless match.empty?
-
60
encoder.text_token match, :content
-
60
break if eos?
-
end
-
-
60
if state.heredoc && self[1] # end of heredoc
-
match = getch
-
match << scan_until(/$/) unless eos?
-
encoder.text_token match, :delimiter unless match.empty?
-
encoder.end_group state.type
-
state = state.next_state
-
next
-
end
-
-
60
case match = getch
-
-
when state.delim
-
15
if state.paren_depth
-
state.paren_depth -= 1
-
if state.paren_depth > 0
-
encoder.text_token match, :content
-
next
-
end
-
end
-
15
encoder.text_token match, :delimiter
-
15
if state.type == :regexp && !eos?
-
match = scan(/#{patterns::REGEXP_MODIFIERS}/o)
-
encoder.text_token match, :modifier unless match.empty?
-
end
-
15
encoder.end_group state.type
-
15
value_expected = false
-
15
state = state.next_state
-
-
when '\\'
-
if state.interpreted
-
if esc = scan(/#{patterns::ESCAPE}/o)
-
encoder.text_token match + esc, :char
-
else
-
encoder.text_token match, :error
-
end
-
else
-
case esc = getch
-
when nil
-
encoder.text_token match, :content
-
when state.delim, '\\'
-
encoder.text_token match + esc, :char
-
else
-
encoder.text_token match + esc, :content
-
end
-
end
-
-
when '#'
-
45
case peek(1)
-
when '{'
-
45
inline_block_stack ||= []
-
45
inline_block_stack << [state, inline_block_curly_depth, heredocs]
-
45
value_expected = true
-
45
state = :initial
-
45
inline_block_curly_depth = 1
-
45
encoder.begin_group :inline
-
45
encoder.text_token match + getch, :inline_delimiter
-
when '$', '@'
-
encoder.text_token match, :escape
-
last_state = state
-
state = :initial
-
else
-
#:nocov:
-
skipped
raise_inspect 'else-case # reached; #%p not handled' % [peek(1)], encoder
-
#:nocov:
-
end
-
-
when state.opening_paren
-
state.paren_depth += 1
-
encoder.text_token match, :content
-
-
else
-
#:nocov
-
raise_inspect 'else-case " reached; %p not handled, state = %p' % [match, state], encoder
-
#:nocov:
-
skipped
-
skipped
end
-
skipped
-
skipped
end
-
skipped
-
skipped
end
-
skipped
-
skipped
# cleaning up
-
skipped
if state.is_a? StringState
-
skipped
encoder.end_group state.type
-
skipped
end
-
skipped
-
skipped
if options[:keep_state]
-
skipped
if state.is_a?(StringState) && state.heredoc
-
skipped
(heredocs ||= []).unshift state
-
skipped
state = :initial
-
skipped
elsif heredocs && heredocs.empty?
-
skipped
heredocs = nil
-
skipped
end
-
skipped
@state = state, heredocs
-
skipped
end
-
skipped
-
skipped
if inline_block_stack
-
skipped
until inline_block_stack.empty?
-
skipped
state, = *inline_block_stack.pop
-
skipped
encoder.end_group :inline
-
skipped
encoder.end_group state.type
-
skipped
end
-
skipped
end
-
skipped
-
skipped
encoder
-
skipped
end
-
skipped
-
skipped
end
-
skipped
-
skipped
end
-
skipped
end
-
# encoding: utf-8
-
1
module CodeRay
-
1
module Scanners
-
-
1
module Ruby::Patterns # :nodoc: all
-
-
1
KEYWORDS = %w[
-
and def end in or unless begin
-
defined? ensure module redo super until
-
BEGIN break do next rescue then
-
when END case else for retry
-
while alias class elsif if not return
-
undef yield
-
]
-
-
# See http://murfy.de/ruby-constants.
-
1
PREDEFINED_CONSTANTS = %w[
-
nil true false self
-
DATA ARGV ARGF ENV
-
FALSE TRUE NIL
-
STDERR STDIN STDOUT
-
TOPLEVEL_BINDING
-
RUBY_COPYRIGHT RUBY_DESCRIPTION RUBY_ENGINE RUBY_PATCHLEVEL
-
RUBY_PLATFORM RUBY_RELEASE_DATE RUBY_REVISION RUBY_VERSION
-
__FILE__ __LINE__ __ENCODING__
-
]
-
-
1
IDENT_KIND = WordList.new(:ident).
-
add(KEYWORDS, :keyword).
-
add(PREDEFINED_CONSTANTS, :predefined_constant)
-
-
1
KEYWORD_NEW_STATE = WordList.new(:initial).
-
add(%w[ def ], :def_expected).
-
add(%w[ undef ], :undef_expected).
-
add(%w[ alias ], :alias_expected).
-
add(%w[ class module ], :module_expected)
-
-
1
IDENT = 'ä'[/[[:alpha:]]/] == 'ä' ? Regexp.new('[[:alpha:]_[^\0-\177]][[:alnum:]_[^\0-\177]]*') : /[^\W\d]\w*/
-
-
1
METHOD_NAME = / #{IDENT} [?!]? /ox
-
1
METHOD_NAME_OPERATOR = /
-
\*\*? # multiplication and power
-
| [-+~]@? # plus, minus, tilde with and without at sign
-
| [\/%&|^`] # division, modulo or format strings, and, or, xor, system
-
| \[\]=? # array getter and setter
-
| << | >> # append or shift left, shift right
-
| <=?>? | >=? # comparison, rocket operator
-
| ===? | =~ # simple equality, case equality, match
-
| ![~=@]? # negation with and without at sign, not-equal and not-match
-
/ox
-
1
METHOD_SUFFIX = / (?: [?!] | = (?![~>]|=(?!>)) ) /x
-
1
METHOD_NAME_EX = / #{IDENT} #{METHOD_SUFFIX}? | #{METHOD_NAME_OPERATOR} /ox
-
1
METHOD_AFTER_DOT = / #{IDENT} [?!]? | #{METHOD_NAME_OPERATOR} /ox
-
1
INSTANCE_VARIABLE = / @ #{IDENT} /ox
-
1
CLASS_VARIABLE = / @@ #{IDENT} /ox
-
1
OBJECT_VARIABLE = / @@? #{IDENT} /ox
-
1
GLOBAL_VARIABLE = / \$ (?: #{IDENT} | [1-9]\d* | 0\w* | [~&+`'=\/,;_.<>!@$?*":\\] | -[a-zA-Z_0-9] ) /ox
-
1
PREFIX_VARIABLE = / #{GLOBAL_VARIABLE} | #{OBJECT_VARIABLE} /ox
-
1
VARIABLE = / @?@? #{IDENT} | #{GLOBAL_VARIABLE} /ox
-
-
1
QUOTE_TO_TYPE = {
-
'`' => :shell,
-
'/'=> :regexp,
-
}
-
1
QUOTE_TO_TYPE.default = :string
-
-
1
REGEXP_MODIFIERS = /[mousenix]*/
-
-
1
DECIMAL = /\d+(?:_\d+)*/
-
1
OCTAL = /0_?[0-7]+(?:_[0-7]+)*/
-
1
HEXADECIMAL = /0x[0-9A-Fa-f]+(?:_[0-9A-Fa-f]+)*/
-
1
BINARY = /0b[01]+(?:_[01]+)*/
-
-
1
EXPONENT = / [eE] [+-]? #{DECIMAL} /ox
-
1
FLOAT_SUFFIX = / #{EXPONENT} | \. #{DECIMAL} #{EXPONENT}? /ox
-
1
FLOAT_OR_INT = / #{DECIMAL} (?: #{FLOAT_SUFFIX} () )? /ox
-
1
NUMERIC = / (?: (?=0) (?: #{OCTAL} | #{HEXADECIMAL} | #{BINARY} ) | #{FLOAT_OR_INT} ) /ox
-
-
1
SYMBOL = /
-
:
-
(?:
-
#{METHOD_NAME_EX}
-
| #{PREFIX_VARIABLE}
-
| ['"]
-
)
-
/ox
-
1
METHOD_NAME_OR_SYMBOL = / #{METHOD_NAME_EX} | #{SYMBOL} /ox
-
-
1
SIMPLE_ESCAPE = /
-
[abefnrstv]
-
| [0-7]{1,3}
-
| x[0-9A-Fa-f]{1,2}
-
| .
-
/mx
-
-
1
CONTROL_META_ESCAPE = /
-
(?: M-|C-|c )
-
(?: \\ (?: M-|C-|c ) )*
-
(?: [^\\] | \\ #{SIMPLE_ESCAPE} )?
-
/mox
-
-
1
ESCAPE = /
-
#{CONTROL_META_ESCAPE} | #{SIMPLE_ESCAPE}
-
/mox
-
-
1
CHARACTER = /
-
\?
-
(?:
-
[^\s\\]
-
| \\ #{ESCAPE}
-
)
-
/mox
-
-
# NOTE: This is not completely correct, but
-
# nobody needs heredoc delimiters ending with \n.
-
1
HEREDOC_OPEN = /
-
<< (-)? # $1 = float
-
(?:
-
( [A-Za-z_0-9]+ ) # $2 = delim
-
|
-
( ["'`\/] ) # $3 = quote, type
-
( [^\n]*? ) \3 # $4 = delim
-
)
-
/mx
-
-
1
RUBYDOC = /
-
=begin (?!\S)
-
.*?
-
(?: \Z | ^=end (?!\S) [^\n]* )
-
/mx
-
-
1
DATA = /
-
__END__$
-
.*?
-
(?: \Z | (?=^\#CODE) )
-
/mx
-
-
1
RUBYDOC_OR_DATA = / #{RUBYDOC} | #{DATA} /xo
-
-
# Checks for a valid value to follow. This enables
-
# value_expected in method calls without parentheses.
-
1
VALUE_FOLLOWS = /
-
(?>[ \t\f\v]+)
-
(?:
-
[%\/][^\s=]
-
| <<-?\S
-
| [-+] \d
-
| #{CHARACTER}
-
)
-
/ox
-
1
KEYWORDS_EXPECTING_VALUE = WordList.new.add(%w[
-
and end in or unless begin
-
defined? ensure redo super until
-
break do next rescue then
-
when case else for retry
-
while elsif if not return
-
yield
-
])
-
-
1
FANCY_STRING_START = / % ( [iIqQrswWx] | (?![a-zA-Z0-9]) ) ([^a-zA-Z0-9]) /x
-
1
FANCY_STRING_KIND = Hash.new(:string).merge({
-
'i' => :symbol,
-
'I' => :symbol,
-
'r' => :regexp,
-
's' => :symbol,
-
'x' => :shell,
-
})
-
1
FANCY_STRING_INTERPRETED = Hash.new(true).merge({
-
'i' => false,
-
'q' => false,
-
's' => false,
-
'w' => false,
-
})
-
-
end
-
-
end
-
end
-
# encoding: utf-8
-
1
module CodeRay
-
1
module Scanners
-
-
1
class Ruby
-
-
class StringState < Struct.new :type, :interpreted, :delim, :heredoc,
-
1
:opening_paren, :paren_depth, :pattern, :next_state # :nodoc: all
-
-
1
CLOSING_PAREN = Hash[ *%w[
-
( )
-
[ ]
-
< >
-
{ }
-
4
] ].each { |k,v| k.freeze; v.freeze } # debug, if I try to change it with <<
-
-
1
STRING_PATTERN = Hash.new do |h, k|
-
1
delim, interpreted = *k
-
1
delim_pattern = Regexp.escape(delim)
-
1
if closing_paren = CLOSING_PAREN[delim]
-
delim_pattern << Regexp.escape(closing_paren)
-
end
-
1
delim_pattern << '\\\\' unless delim == '\\'
-
-
# special_escapes =
-
# case interpreted
-
# when :regexp_symbols
-
# '| [|?*+(){}\[\].^$]'
-
# end
-
-
if interpreted && delim != '#'
-
1
/ (?= [#{delim_pattern}] | \# [{$@] ) /mx
-
else
-
/ (?= [#{delim_pattern}] ) /mx
-
1
end.tap do |pattern|
-
1
h[k] = pattern if (delim.respond_to?(:ord) ? delim.ord : delim[0]) < 256
-
end
-
end
-
-
1
def initialize kind, interpreted, delim, heredoc = false
-
15
if heredoc
-
pattern = heredoc_pattern delim, interpreted, heredoc == :indented
-
delim = nil
-
else
-
15
pattern = STRING_PATTERN[ [delim, interpreted] ]
-
15
if closing_paren = CLOSING_PAREN[delim]
-
opening_paren = delim
-
delim = closing_paren
-
paren_depth = 1
-
end
-
end
-
15
super kind, interpreted, delim, heredoc, opening_paren, paren_depth, pattern, :initial
-
end
-
-
1
def heredoc_pattern delim, interpreted, indented
-
# delim = delim.dup # workaround for old Ruby
-
delim_pattern = Regexp.escape(delim)
-
delim_pattern = / (?:\A|\n) #{ '(?>[ \t]*)' if indented } #{ Regexp.new delim_pattern } $ /x
-
if interpreted
-
/ (?= #{delim_pattern}() | \\ | \# [{$@] ) /mx # $1 set == end of heredoc
-
else
-
/ (?= #{delim_pattern}() | \\ ) /mx
-
end
-
end
-
-
end
-
-
end
-
-
end
-
end
-
1
module CodeRay
-
-
# The Tokens class represents a list of tokens returned from
-
# a Scanner. It's actually just an Array with a few helper methods.
-
#
-
# A token itself is not a special object, just two elements in an Array:
-
# * the _token_ _text_ (the original source of the token in a String) or
-
# a _token_ _action_ (begin_group, end_group, begin_line, end_line)
-
# * the _token_ _kind_ (a Symbol representing the type of the token)
-
#
-
# It looks like this:
-
#
-
# ..., '# It looks like this', :comment, ...
-
# ..., '3.1415926', :float, ...
-
# ..., '$^', :error, ...
-
#
-
# Some scanners also yield sub-tokens, represented by special
-
# token actions, for example :begin_group and :end_group.
-
#
-
# The Ruby scanner, for example, splits "a string" into:
-
#
-
# [
-
# :begin_group, :string,
-
# '"', :delimiter,
-
# 'a string', :content,
-
# '"', :delimiter,
-
# :end_group, :string
-
# ]
-
#
-
# Tokens can be used to save the output of a Scanners in a simple
-
# Ruby object that can be send to an Encoder later:
-
#
-
# tokens = CodeRay.scan('price = 2.59', :ruby).tokens
-
# tokens.encode(:html)
-
# tokens.html
-
# CodeRay.encoder(:html).encode_tokens(tokens)
-
#
-
# Tokens gives you the power to handle pre-scanned code very easily:
-
# You can serialize it to a JSON string and store it in a database, pass it
-
# around to encode it more than once, send it to other algorithms...
-
1
class Tokens < Array
-
-
# The Scanner instance that created the tokens.
-
1
attr_accessor :scanner
-
-
# Encode the tokens using encoder.
-
#
-
# encoder can be
-
# * a plugin name like :html oder 'statistic'
-
# * an Encoder object
-
#
-
# options are passed to the encoder.
-
1
def encode encoder, options = {}
-
encoder = Encoders[encoder].new options if encoder.respond_to? :to_sym
-
encoder.encode_tokens self, options
-
end
-
-
# Turn tokens into a string by concatenating them.
-
1
def to_s
-
encode CodeRay::Encoders::Encoder.new
-
end
-
-
# Redirects unknown methods to encoder calls.
-
#
-
# For example, if you call +tokens.html+, the HTML encoder
-
# is used to highlight the tokens.
-
1
def method_missing meth, options = {}
-
encode meth, options
-
rescue PluginHost::PluginNotFound
-
super
-
end
-
-
# Split the tokens into parts of the given +sizes+.
-
#
-
# The result will be an Array of Tokens objects. The parts have
-
# the text size specified by the parameter. In addition, each
-
# part closes all opened tokens. This is useful to insert tokens
-
# betweem them.
-
#
-
# This method is used by @Scanner#tokenize@ when called with an Array
-
# of source strings. The Diff encoder uses it for inline highlighting.
-
1
def split_into_parts *sizes
-
return Array.new(sizes.size) { Tokens.new } if size == 2 && first == ''
-
parts = []
-
opened = []
-
content = nil
-
part = Tokens.new
-
part_size = 0
-
size = sizes.first
-
i = 0
-
for item in self
-
case content
-
when nil
-
content = item
-
when String
-
if size && part_size + content.size > size # token must be cut
-
if part_size < size # some part of the token goes into this part
-
content = content.dup # content may no be safe to change
-
part << content.slice!(0, size - part_size) << item
-
end
-
# close all open groups and lines...
-
closing = opened.reverse.flatten.map do |content_or_kind|
-
case content_or_kind
-
when :begin_group
-
:end_group
-
when :begin_line
-
:end_line
-
else
-
content_or_kind
-
end
-
end
-
part.concat closing
-
begin
-
parts << part
-
part = Tokens.new
-
size = sizes[i += 1]
-
end until size.nil? || size > 0
-
# ...and open them again.
-
part.concat opened.flatten
-
part_size = 0
-
redo unless content.empty?
-
else
-
part << content << item
-
part_size += content.size
-
end
-
content = nil
-
when Symbol
-
case content
-
when :begin_group, :begin_line
-
opened << [content, item]
-
when :end_group, :end_line
-
opened.pop
-
else
-
raise ArgumentError, 'Unknown token action: %p, kind = %p' % [content, item]
-
end
-
part << content << item
-
content = nil
-
else
-
raise ArgumentError, 'Token input junk: %p, kind = %p' % [content, item]
-
end
-
end
-
parts << part
-
parts << Tokens.new while parts.size < sizes.size
-
parts
-
end
-
-
# Return the actual number of tokens.
-
1
def count
-
size / 2
-
end
-
-
1
alias text_token push
-
15
def begin_group kind; push :begin_group, kind end
-
15
def end_group kind; push :end_group, kind end
-
1
def begin_line kind; push :begin_line, kind end
-
1
def end_line kind; push :end_line, kind end
-
1
alias tokens concat
-
-
end
-
-
end
-
1
module CodeRay
-
-
# The result of a scan operation is a TokensProxy, but should act like Tokens.
-
#
-
# This proxy makes it possible to use the classic CodeRay.scan.encode API
-
# while still providing the benefits of direct streaming.
-
1
class TokensProxy
-
-
1
attr_accessor :input, :lang, :options, :block
-
-
# Create a new TokensProxy with the arguments of CodeRay.scan.
-
1
def initialize input, lang, options = {}, block = nil
-
94
@input = input
-
94
@lang = lang
-
94
@options = options
-
94
@block = block
-
end
-
-
# Call CodeRay.encode if +encoder+ is a Symbol;
-
# otherwise, convert the receiver to tokens and call encoder.encode_tokens.
-
1
def encode encoder, options = {}
-
81
if encoder.respond_to? :to_sym
-
81
CodeRay.encode(input, lang, encoder, options)
-
else
-
encoder.encode_tokens tokens, options
-
end
-
end
-
-
# Tries to call encode;
-
# delegates to tokens otherwise.
-
1
def method_missing method, *args, &blk
-
81
encode method.to_sym, *args
-
rescue PluginHost::PluginNotFound
-
tokens.send(method, *args, &blk)
-
end
-
-
# The (cached) result of the tokenized input; a Tokens instance.
-
1
def tokens
-
13
@tokens ||= scanner.tokenize(input)
-
end
-
-
# A (cached) scanner instance to use for the scan task.
-
1
def scanner
-
13
@scanner ||= CodeRay.scanner(lang, options, &block)
-
end
-
-
# Overwrite Struct#each.
-
1
def each *args, &blk
-
tokens.each(*args, &blk)
-
self
-
end
-
-
end
-
-
end
-
1
module CodeRay
-
1
VERSION = '1.1.0'
-
end
-
# (C) John Mair (banisterfiend) 2011
-
# MIT License
-
-
1
direc = File.dirname(__FILE__)
-
-
1
require "#{direc}/method_source/version"
-
1
require "#{direc}/method_source/source_location"
-
1
require "#{direc}/method_source/code_helpers"
-
-
1
module MethodSource
-
1
extend MethodSource::CodeHelpers
-
-
# An Exception to mark errors that were raised trying to find the source from
-
# a given source_location.
-
#
-
1
class SourceNotFoundError < StandardError; end
-
-
# Helper method responsible for extracting method body.
-
# Defined here to avoid polluting `Method` class.
-
# @param [Array] source_location The array returned by Method#source_location
-
# @param [String] method_name
-
# @return [String] The method body
-
1
def self.source_helper(source_location, name=nil)
-
raise SourceNotFoundError, "Could not locate source for #{name}!" unless source_location
-
file, line = *source_location
-
-
expression_at(lines_for(file), line)
-
rescue SyntaxError => e
-
raise SourceNotFoundError, "Could not parse source for #{name}: #{e.message}"
-
end
-
-
# Helper method responsible for opening source file and buffering up
-
# the comments for a specified method. Defined here to avoid polluting
-
# `Method` class.
-
# @param [Array] source_location The array returned by Method#source_location
-
# @param [String] method_name
-
# @return [String] The comments up to the point of the method.
-
1
def self.comment_helper(source_location, name=nil)
-
raise SourceNotFoundError, "Could not locate source for #{name}!" unless source_location
-
file, line = *source_location
-
-
comment_describing(lines_for(file), line)
-
end
-
-
# Load a memoized copy of the lines in a file.
-
#
-
# @param [String] file_name
-
# @param [String] method_name
-
# @return [Array<String>] the contents of the file
-
# @raise [SourceNotFoundError]
-
1
def self.lines_for(file_name, name=nil)
-
@lines_for_file ||= {}
-
@lines_for_file[file_name] ||= File.readlines(file_name)
-
rescue Errno::ENOENT => e
-
raise SourceNotFoundError, "Could not load source for #{name}: #{e.message}"
-
end
-
-
# @deprecated — use MethodSource::CodeHelpers#complete_expression?
-
1
def self.valid_expression?(str)
-
complete_expression?(str)
-
rescue SyntaxError
-
false
-
end
-
-
# @deprecated — use MethodSource::CodeHelpers#expression_at
-
1
def self.extract_code(source_location)
-
source_helper(source_location)
-
end
-
-
# This module is to be included by `Method` and `UnboundMethod` and
-
# provides the `#source` functionality
-
1
module MethodExtensions
-
-
# We use the included hook to patch Method#source on rubinius.
-
# We need to use the included hook as Rubinius defines a `source`
-
# on Method so including a module will have no effect (as it's
-
# higher up the MRO).
-
# @param [Class] klass The class that includes the module.
-
1
def self.included(klass)
-
3
if klass.method_defined?(:source) && Object.const_defined?(:RUBY_ENGINE) &&
-
RUBY_ENGINE =~ /rbx/
-
-
klass.class_eval do
-
orig_source = instance_method(:source)
-
-
define_method(:source) do
-
begin
-
super
-
rescue
-
orig_source.bind(self).call
-
end
-
end
-
-
end
-
end
-
end
-
-
# Return the sourcecode for the method as a string
-
# @return [String] The method sourcecode as a string
-
# @raise SourceNotFoundException
-
#
-
# @example
-
# Set.instance_method(:clear).source.display
-
# =>
-
# def clear
-
# @hash.clear
-
# self
-
# end
-
1
def source
-
MethodSource.source_helper(source_location, defined?(name) ? name : inspect)
-
end
-
-
# Return the comments associated with the method as a string.
-
# @return [String] The method's comments as a string
-
# @raise SourceNotFoundException
-
#
-
# @example
-
# Set.instance_method(:clear).comment.display
-
# =>
-
# # Removes all elements and returns self.
-
1
def comment
-
MethodSource.comment_helper(source_location, defined?(name) ? name : inspect)
-
end
-
end
-
end
-
-
1
class Method
-
1
include MethodSource::SourceLocation::MethodExtensions
-
1
include MethodSource::MethodExtensions
-
end
-
-
1
class UnboundMethod
-
1
include MethodSource::SourceLocation::UnboundMethodExtensions
-
1
include MethodSource::MethodExtensions
-
end
-
-
1
class Proc
-
1
include MethodSource::SourceLocation::ProcExtensions
-
1
include MethodSource::MethodExtensions
-
end
-
-
1
module MethodSource
-
-
1
module CodeHelpers
-
# Retrieve the first expression starting on the given line of the given file.
-
#
-
# This is useful to get module or method source code.
-
#
-
# @param [Array<String>, File, String] file The file to parse, either as a File or as
-
# @param [Fixnum] line_number The line number at which to look.
-
# NOTE: The first line in a file is
-
# line 1!
-
# @param [Hash] options The optional configuration parameters.
-
# @option options [Boolean] :strict If set to true, then only completely
-
# valid expressions are returned. Otherwise heuristics are used to extract
-
# expressions that may have been valid inside an eval.
-
# @option options [Fixnum] :consume A number of lines to automatically
-
# consume (add to the expression buffer) without checking for validity.
-
# @return [String] The first complete expression
-
# @raise [SyntaxError] If the first complete expression can't be identified
-
1
def expression_at(file, line_number, options={})
-
4
options = {
-
:strict => false,
-
:consume => 0
-
}.merge!(options)
-
-
4
lines = file.is_a?(Array) ? file : file.each_line.to_a
-
-
4
relevant_lines = lines[(line_number - 1)..-1] || []
-
-
4
extract_first_expression(relevant_lines, options[:consume])
-
rescue SyntaxError => e
-
raise if options[:strict]
-
-
begin
-
extract_first_expression(relevant_lines) do |code|
-
code.gsub(/\#\{.*?\}/, "temp")
-
end
-
rescue SyntaxError
-
raise e
-
end
-
end
-
-
# Retrieve the comment describing the expression on the given line of the given file.
-
#
-
# This is useful to get module or method documentation.
-
#
-
# @param [Array<String>, File, String] file The file to parse, either as a File or as
-
# a String or an Array of lines.
-
# @param [Fixnum] line_number The line number at which to look.
-
# NOTE: The first line in a file is line 1!
-
# @return [String] The comment
-
1
def comment_describing(file, line_number)
-
lines = file.is_a?(Array) ? file : file.each_line.to_a
-
-
extract_last_comment(lines[0..(line_number - 2)])
-
end
-
-
# Determine if a string of code is a complete Ruby expression.
-
# @param [String] code The code to validate.
-
# @return [Boolean] Whether or not the code is a complete Ruby expression.
-
# @raise [SyntaxError] Any SyntaxError that does not represent incompleteness.
-
# @example
-
# complete_expression?("class Hello") #=> false
-
# complete_expression?("class Hello; end") #=> true
-
# complete_expression?("class 123") #=> SyntaxError: unexpected tINTEGER
-
1
def complete_expression?(str)
-
63
old_verbose = $VERBOSE
-
63
$VERBOSE = nil
-
-
63
catch(:valid) do
-
63
eval("BEGIN{throw :valid}\n#{str}")
-
end
-
-
# Assert that a line which ends with a , or \ is incomplete.
-
14
str !~ /[,\\]\s*\z/
-
rescue IncompleteExpression
-
49
false
-
ensure
-
63
$VERBOSE = old_verbose
-
end
-
-
1
private
-
-
# Get the first expression from the input.
-
#
-
# @param [Array<String>] lines
-
# @param [Fixnum] consume A number of lines to automatically
-
# consume (add to the expression buffer) without checking for validity.
-
# @yield a clean-up function to run before checking for complete_expression
-
# @return [String] a valid ruby expression
-
# @raise [SyntaxError]
-
1
def extract_first_expression(lines, consume=0, &block)
-
4
code = consume.zero? ? "" : lines.slice!(0..(consume - 1)).join
-
-
4
lines.each do |v|
-
39
code << v
-
39
return code if complete_expression?(block ? block.call(code) : code)
-
end
-
raise SyntaxError, "unexpected $end"
-
end
-
-
# Get the last comment from the input.
-
#
-
# @param [Array<String>] lines
-
# @return [String]
-
1
def extract_last_comment(lines)
-
buffer = ""
-
-
lines.each do |line|
-
# Add any line that is a valid ruby comment,
-
# but clear as soon as we hit a non comment line.
-
if (line =~ /^\s*#/) || (line =~ /^\s*$/)
-
buffer << line.lstrip
-
else
-
buffer.replace("")
-
end
-
end
-
-
buffer
-
end
-
-
# An exception matcher that matches only subsets of SyntaxErrors that can be
-
# fixed by adding more input to the buffer.
-
1
module IncompleteExpression
-
1
GENERIC_REGEXPS = [
-
/unexpected (\$end|end-of-file|end-of-input|END_OF_FILE)/, # mri, jruby, ruby-2.0, ironruby
-
/embedded document meets end of file/, # =begin
-
/unterminated (quoted string|string|regexp) meets end of file/, # "quoted string" is ironruby
-
/can't find string ".*" anywhere before EOF/, # rbx and jruby
-
/missing 'end' for/, /expecting kWHEN/ # rbx
-
]
-
-
1
RBX_ONLY_REGEXPS = [
-
/expecting '[})\]]'(?:$|:)/, /expecting keyword_end/
-
]
-
-
1
def self.===(ex)
-
49
return false unless SyntaxError === ex
-
49
case ex.message
-
when *GENERIC_REGEXPS
-
49
true
-
when *RBX_ONLY_REGEXPS
-
rbx?
-
else
-
false
-
end
-
end
-
-
1
def self.rbx?
-
RbConfig::CONFIG['ruby_install_name'] == 'rbx'
-
end
-
end
-
end
-
end
-
1
module MethodSource
-
1
module ReeSourceLocation
-
# Ruby enterprise edition provides all the information that's
-
# needed, in a slightly different way.
-
1
def source_location
-
[__file__, __line__] rescue nil
-
end
-
end
-
-
1
module SourceLocation
-
1
module MethodExtensions
-
1
if Proc.method_defined? :__file__
-
include ReeSourceLocation
-
-
elsif defined?(RUBY_ENGINE) && RUBY_ENGINE =~ /jruby/
-
require 'java'
-
-
# JRuby version source_location hack
-
# @return [Array] A two element array containing the source location of the method
-
def source_location
-
to_java.source_location(Thread.current.to_java.getContext())
-
end
-
else
-
-
-
1
def trace_func(event, file, line, id, binding, classname)
-
return unless event == 'call'
-
set_trace_func nil
-
-
@file, @line = file, line
-
raise :found
-
end
-
-
1
private :trace_func
-
-
# Return the source location of a method for Ruby 1.8.
-
# @return [Array] A two element array. First element is the
-
# file, second element is the line in the file where the
-
# method definition is found.
-
1
def source_location
-
if @file.nil?
-
args =[*(1..(arity<-1 ? -arity-1 : arity ))]
-
-
set_trace_func method(:trace_func).to_proc
-
call(*args) rescue nil
-
set_trace_func nil
-
@file = File.expand_path(@file) if @file && File.exist?(File.expand_path(@file))
-
end
-
[@file, @line] if @file
-
end
-
end
-
end
-
-
1
module ProcExtensions
-
1
if Proc.method_defined? :__file__
-
include ReeSourceLocation
-
-
elsif defined?(RUBY_ENGINE) && RUBY_ENGINE =~ /rbx/
-
-
# Return the source location for a Proc (Rubinius only)
-
# @return [Array] A two element array. First element is the
-
# file, second element is the line in the file where the
-
# proc definition is found.
-
def source_location
-
[block.file.to_s, block.line]
-
end
-
else
-
-
# Return the source location for a Proc (in implementations
-
# without Proc#source_location)
-
# @return [Array] A two element array. First element is the
-
# file, second element is the line in the file where the
-
# proc definition is found.
-
1
def source_location
-
self.to_s =~ /@(.*):(\d+)/
-
[$1, $2.to_i]
-
end
-
end
-
end
-
-
1
module UnboundMethodExtensions
-
1
if Proc.method_defined? :__file__
-
include ReeSourceLocation
-
-
elsif defined?(RUBY_ENGINE) && RUBY_ENGINE =~ /jruby/
-
require 'java'
-
-
# JRuby version source_location hack
-
# @return [Array] A two element array containing the source location of the method
-
def source_location
-
to_java.source_location(Thread.current.to_java.getContext())
-
end
-
-
else
-
-
-
# Return the source location of an instance method for Ruby 1.8.
-
# @return [Array] A two element array. First element is the
-
# file, second element is the line in the file where the
-
# method definition is found.
-
1
def source_location
-
klass = case owner
-
when Class
-
owner
-
when Module
-
method_owner = owner
-
Class.new { include(method_owner) }
-
end
-
-
# deal with immediate values
-
case
-
when klass == Symbol
-
return :a.method(name).source_location
-
when klass == Fixnum
-
return 0.method(name).source_location
-
when klass == TrueClass
-
return true.method(name).source_location
-
when klass == FalseClass
-
return false.method(name).source_location
-
when klass == NilClass
-
return nil.method(name).source_location
-
end
-
-
begin
-
Object.instance_method(:method).bind(klass.allocate).call(name).source_location
-
rescue TypeError
-
-
# Assume we are dealing with a Singleton Class:
-
# 1. Get the instance object
-
# 2. Forward the source_location lookup to the instance
-
instance ||= ObjectSpace.each_object(owner).first
-
Object.instance_method(:method).bind(instance).call(name).source_location
-
end
-
end
-
end
-
end
-
end
-
end
-
1
module MethodSource
-
1
VERSION = "0.8.2"
-
end
-
# (C) John Mair (banisterfiend) 2013
-
# MIT License
-
#
-
1
require 'pp'
-
-
1
require 'pry/input_lock'
-
1
require 'pry/exceptions'
-
1
require 'pry/helpers/base_helpers'
-
1
require 'pry/hooks'
-
1
require 'forwardable'
-
-
1
class Pry
-
# The default hooks - display messages when beginning and ending Pry sessions.
-
1
DEFAULT_HOOKS = Pry::Hooks.new.add_hook(:before_session, :default) do |out, target, _pry_|
-
4
next if _pry_.quiet?
-
4
_pry_.run_command("whereami --quiet")
-
end
-
-
# The default print
-
1
DEFAULT_PRINT = proc do |output, value, _pry_|
-
1
_pry_.pager.open do |pager|
-
1
pager.print _pry_.config.output_prefix
-
1
Pry::ColorPrinter.pp(value, pager, Pry::Terminal.width! - 1)
-
end
-
end
-
-
# may be convenient when working with enormous objects and
-
# pretty_print is too slow
-
1
SIMPLE_PRINT = proc do |output, value|
-
begin
-
output.puts value.inspect
-
rescue RescuableException
-
output.puts "unknown"
-
end
-
end
-
-
# useful when playing with truly enormous objects
-
1
CLIPPED_PRINT = proc do |output, value|
-
output.puts Pry.view_clip(value, id: true)
-
end
-
-
# Will only show the first line of the backtrace
-
1
DEFAULT_EXCEPTION_HANDLER = proc do |output, exception, _|
-
2
if UserError === exception && SyntaxError === exception
-
output.puts "SyntaxError: #{exception.message.sub(/.*syntax error, */m, '')}"
-
else
-
2
output.puts "#{exception.class}: #{exception.message}"
-
2
output.puts "from #{exception.backtrace.first}"
-
end
-
end
-
-
1
DEFAULT_PROMPT_NAME = 'pry'
-
-
# The default prompt; includes the target and nesting level
-
1
DEFAULT_PROMPT = [
-
proc { |target_self, nest_level, pry|
-
10
"[#{pry.input_array.size}] #{pry.config.prompt_name}(#{Pry.view_clip(target_self)})#{":#{nest_level}" unless nest_level.zero?}> "
-
},
-
-
proc { |target_self, nest_level, pry|
-
14
"[#{pry.input_array.size}] #{pry.config.prompt_name}(#{Pry.view_clip(target_self)})#{":#{nest_level}" unless nest_level.zero?}* "
-
}
-
]
-
-
1
DEFAULT_PROMPT_SAFE_OBJECTS = [String, Numeric, Symbol, nil, true, false]
-
-
# A simple prompt - doesn't display target or nesting level
-
1
SIMPLE_PROMPT = [proc { ">> " }, proc { " | " }]
-
-
1
NO_PROMPT = [proc { '' }, proc { '' }]
-
-
1
SHELL_PROMPT = [
-
proc { |target_self, _, _pry_| "#{_pry_.config.prompt_name} #{Pry.view_clip(target_self)}:#{Dir.pwd} $ " },
-
proc { |target_self, _, _pry_| "#{_pry_.config.prompt_name} #{Pry.view_clip(target_self)}:#{Dir.pwd} * " }
-
]
-
-
# A prompt that includes the full object path as well as
-
# input/output (_in_ and _out_) information. Good for navigation.
-
1
NAV_PROMPT = [
-
proc do |_, _, _pry_|
-
tree = _pry_.binding_stack.map { |b| Pry.view_clip(b.eval("self")) }.join " / "
-
"[#{_pry_.input_array.count}] (#{_pry_.config.prompt_name}) #{tree}: #{_pry_.binding_stack.size - 1}> "
-
end,
-
proc do |_, _, _pry_|
-
tree = _pry_.binding_stack.map { |b| Pry.view_clip(b.eval("self")) }.join " / "
-
"[#{_pry_.input_array.count}] (#{ _pry_.config.prompt_name}) #{tree}: #{_pry_.binding_stack.size - 1}* "
-
end,
-
]
-
-
# Deal with the ^D key being pressed. Different behaviour in different cases:
-
# 1. In an expression behave like `!` command.
-
# 2. At top-level session behave like `exit` command.
-
# 3. In a nested session behave like `cd ..`.
-
1
DEFAULT_CONTROL_D_HANDLER = proc do |eval_string, _pry_|
-
if !eval_string.empty?
-
eval_string.replace('') # Clear input buffer.
-
elsif _pry_.binding_stack.one?
-
_pry_.binding_stack.clear
-
throw(:breakout)
-
else
-
# Otherwise, saves current binding stack as old stack and pops last
-
# binding out of binding stack (the old stack still has that binding).
-
_pry_.command_state["cd"] ||= Pry::Config.from_hash({}) # FIXME
-
_pry_.command_state['cd'].old_stack = _pry_.binding_stack.dup
-
_pry_.binding_stack.pop
-
end
-
end
-
-
1
DEFAULT_SYSTEM = proc do |output, cmd, _|
-
if !system(cmd)
-
output.puts "Error: there was a problem executing system command: #{cmd}"
-
end
-
end
-
-
# Store the current working directory. This allows show-source etc. to work if
-
# your process has changed directory since boot. [Issue #675]
-
1
INITIAL_PWD = Dir.pwd
-
-
# This is to keep from breaking under Rails 3.2 for people who are doing that
-
# IRB = Pry thing.
-
1
module ExtendCommandBundle; end
-
end
-
-
1
require 'method_source'
-
1
require 'shellwords'
-
1
require 'stringio'
-
1
require 'coderay'
-
1
require 'slop'
-
1
require 'rbconfig'
-
1
require 'tempfile'
-
1
require 'pathname'
-
-
1
require 'pry/version'
-
1
require 'pry/repl'
-
1
require 'pry/rbx_path'
-
1
require 'pry/code'
-
1
require 'pry/history_array'
-
1
require 'pry/helpers'
-
1
require 'pry/code_object'
-
1
require 'pry/method'
-
1
require 'pry/wrapped_module'
-
1
require 'pry/history'
-
1
require 'pry/command'
-
1
require 'pry/command_set'
-
1
require 'pry/commands'
-
1
require 'pry/plugins'
-
1
require 'pry/core_extensions'
-
1
require 'pry/pry_class'
-
1
require 'pry/pry_instance'
-
1
require 'pry/cli'
-
1
require 'pry/color_printer'
-
1
require 'pry/pager'
-
1
require 'pry/terminal'
-
1
require 'pry/editor'
-
1
require 'pry/rubygem'
-
1
require "pry/indent"
-
1
require "pry/last_exception"
-
1
require "pry/prompt"
-
1
require "pry/inspector"
-
1
require 'pry/object_path'
-
1
require 'pry/output'
-
1
class Pry
-
-
# Manage the processing of command line options
-
1
class CLI
-
-
1
NoOptionsError = Class.new(StandardError)
-
-
1
class << self
-
-
# @return [Proc] The Proc defining the valid command line options.
-
1
attr_accessor :options
-
-
# @return [Array] The Procs that process the parsed options. Plugins can
-
# utilize this facility in order to add and process their own Pry
-
# options.
-
1
attr_accessor :option_processors
-
-
# @return [Array<String>] The input array of strings to process
-
# as CLI options.
-
1
attr_accessor :input_args
-
-
# Add another set of CLI options (a Slop block)
-
1
def add_options(&block)
-
1
if options
-
old_options = options
-
self.options = proc do
-
instance_exec(&old_options)
-
instance_exec(&block)
-
end
-
else
-
1
self.options = block
-
end
-
-
1
self
-
end
-
-
# Bring in options defined in plugins
-
1
def add_plugin_options
-
1
Pry.plugins.values.each do |plugin|
-
plugin.load_cli_options
-
end
-
-
1
self
-
end
-
-
# Add a block responsible for processing parsed options.
-
1
def add_option_processor(&block)
-
1
self.option_processors ||= []
-
1
option_processors << block
-
-
1
self
-
end
-
-
# Clear `options` and `option_processors`
-
1
def reset
-
1
self.options = nil
-
1
self.option_processors = nil
-
end
-
-
1
def parse_options(args=ARGV)
-
unless options
-
raise NoOptionsError, "No command line options defined! Use Pry::CLI.add_options to add command line options."
-
end
-
-
self.input_args = args
-
-
begin
-
opts = Slop.parse!(
-
args,
-
:help => true,
-
:multiple_switches => false,
-
:strict => true,
-
&options
-
)
-
rescue Slop::InvalidOptionError
-
# Display help message on unknown switches and exit.
-
puts Slop.new(&options)
-
exit
-
end
-
-
# Option processors are optional.
-
if option_processors
-
option_processors.each { |processor| processor.call(opts) }
-
end
-
-
self
-
end
-
-
end
-
-
1
reset
-
end
-
end
-
-
-
# String that is built to be executed on start (created by -e and -exec switches)
-
1
exec_string = ""
-
-
# Bring in options defined by plugins
-
Slop.new do
-
1
on "no-plugins" do
-
Pry.config.should_load_plugins = false
-
end
-
1
end.parse(ARGV.dup)
-
-
1
if Pry.config.should_load_plugins
-
1
Pry::CLI.add_plugin_options
-
end
-
-
# The default Pry command line options (before plugin options are included)
-
Pry::CLI.add_options do
-
banner %{Usage: pry [OPTIONS]
-
Start a Pry session.
-
See http://pryrepl.org/ for more information.
-
Copyright (c) 2013 John Mair (banisterfiend)
-
--
-
}
-
on :e, :exec=, "A line of code to execute in context before the session starts" do |input|
-
exec_string << input << "\n"
-
end
-
-
on "no-pager", "Disable pager for long output" do
-
Pry.config.pager = false
-
end
-
-
on "no-history", "Disable history loading" do
-
Pry.config.history.should_load = false
-
end
-
-
on "no-color", "Disable syntax highlighting for session" do
-
Pry.config.color = false
-
end
-
-
on :f, "Suppress loading of ~/.pryrc and ./.pryrc" do
-
Pry.config.should_load_rc = false
-
Pry.config.should_load_local_rc = false
-
end
-
-
on :s, "select-plugin=", "Only load specified plugin (and no others)." do |plugin_name|
-
Pry.config.should_load_plugins = false
-
Pry.plugins[plugin_name].activate!
-
end
-
-
on :d, "disable-plugin=", "Disable a specific plugin." do |plugin_name|
-
Pry.plugins[plugin_name].disable!
-
end
-
-
on "no-plugins", "Suppress loading of plugins." do
-
Pry.config.should_load_plugins = false
-
end
-
-
on "plugins", "List installed plugins." do
-
puts "Installed Plugins:"
-
puts "--"
-
Pry.locate_plugins.each do |plugin|
-
puts "#{plugin.name}".ljust(18) << plugin.spec.summary
-
end
-
exit
-
end
-
-
on "simple-prompt", "Enable simple prompt mode" do
-
Pry.config.prompt = Pry::SIMPLE_PROMPT
-
end
-
-
on "noprompt", "No prompt mode" do
-
Pry.config.prompt = Pry::NO_PROMPT
-
end
-
-
on :r, :require=, "`require` a Ruby script at startup" do |file|
-
Pry.config.requires << file
-
end
-
-
on :I=, "Add a path to the $LOAD_PATH", :as => Array, :delimiter => ":" do |load_path|
-
load_path.map! do |path|
-
/\A\.\// =~ path ? path : File.expand_path(path)
-
end
-
-
$LOAD_PATH.unshift(*load_path)
-
end
-
-
on "gem", "Shorthand for -I./lib -rgemname" do |load_path|
-
$LOAD_PATH.unshift("./lib")
-
Dir["./lib/*.rb"].each do |file|
-
Pry.config.requires << file
-
end
-
end
-
-
on :v, :version, "Display the Pry version" do
-
puts "Pry version #{Pry::VERSION} on Ruby #{RUBY_VERSION}"
-
exit
-
end
-
-
on(:c, :context=,
-
"Start the session in the specified context. Equivalent to `context.pry` in a session.",
-
:default => "Pry.toplevel_binding"
-
)
-
1
end.add_option_processor do |opts|
-
-
exit if opts.help?
-
-
# invoked via cli
-
Pry.cli = true
-
-
# create the actual context
-
if opts[:context]
-
Pry.initial_session_setup
-
context = Pry.binding_for(eval(opts[:context]))
-
else
-
context = Pry.toplevel_binding
-
end
-
-
if Pry::CLI.input_args.any? && Pry::CLI.input_args != ["pry"]
-
full_name = File.expand_path(Pry::CLI.input_args.first)
-
Pry.load_file_through_repl(full_name)
-
exit
-
end
-
-
# Start the session (running any code passed with -e, if there is any)
-
Pry.start(context, :input => StringIO.new(exec_string))
-
end
-
1
require 'pry/code/loc'
-
1
require 'pry/code/code_range'
-
1
require 'pry/code/code_file'
-
-
1
class Pry
-
1
class << self
-
# Convert the given object into an instance of `Pry::Code`, if it isn't
-
# already one.
-
#
-
# @param [Code, Method, UnboundMethod, Proc, Pry::Method, String, Array,
-
# IO] obj
-
1
def Code(obj)
-
case obj
-
when Code
-
obj
-
when ::Method, UnboundMethod, Proc, Pry::Method
-
Code.from_method(obj)
-
else
-
Code.new(obj)
-
end
-
end
-
end
-
-
# `Pry::Code` is a class that encapsulates lines of source code and their
-
# line numbers and formats them for terminal output. It can read from a file
-
# or method definition or be instantiated with a `String` or an `Array`.
-
#
-
# In general, the formatting methods in `Code` return a new `Code` object
-
# which will format the text as specified when `#to_s` is called. This allows
-
# arbitrary chaining of formatting methods without mutating the original
-
# object.
-
1
class Code
-
1
class << self
-
1
include MethodSource::CodeHelpers
-
-
# Instantiate a `Code` object containing code loaded from a file or
-
# Pry's line buffer.
-
#
-
# @param [String] filename The name of a file, or "(pry)".
-
# @param [Symbol] code_type The type of code the file contains.
-
# @return [Code]
-
1
def from_file(filename, code_type = nil)
-
4
code_file = CodeFile.new(filename, code_type)
-
4
new(code_file.code, 1, code_file.code_type)
-
end
-
-
# Instantiate a `Code` object containing code extracted from a
-
# `::Method`, `UnboundMethod`, `Proc`, or `Pry::Method` object.
-
#
-
# @param [::Method, UnboundMethod, Proc, Pry::Method] meth The method
-
# object.
-
# @param [Integer, nil] start_line The line number to start on, or nil to
-
# use the method's original line numbers.
-
# @return [Code]
-
1
def from_method(meth, start_line = nil)
-
4
meth = Pry::Method(meth)
-
4
start_line ||= meth.source_line || 1
-
4
new(meth.source, start_line, meth.source_type)
-
end
-
-
# Attempt to extract the source code for module (or class) `mod`.
-
#
-
# @param [Module, Class] mod The module (or class) of interest.
-
# @param [Integer] candidate_rank The module candidate (by rank)
-
# to use (see `Pry::WrappedModule::Candidate` for more information).
-
# @param [Integer, nil] start_line The line number to start on, or nil to
-
# use the method's original line numbers.
-
# @return [Code]
-
1
def from_module(mod, candidate_rank = 0, start_line=nil)
-
candidate = Pry::WrappedModule(mod).candidate(candidate_rank)
-
start_line ||= candidate.line
-
new(candidate.source, start_line, :ruby)
-
end
-
end
-
-
# @return [Symbol] The type of code stored in this wrapper.
-
1
attr_accessor :code_type
-
-
# Instantiate a `Code` object containing code from the given `Array`,
-
# `String`, or `IO`. The first line will be line 1 unless specified
-
# otherwise. If you need non-contiguous line numbers, you can create an
-
# empty `Code` object and then use `#push` to insert the lines.
-
#
-
# @param [Array<String>, String, IO] lines
-
# @param [Integer?] start_line
-
# @param [Symbol?] code_type
-
1
def initialize(lines = [], start_line = 1, code_type = :ruby)
-
8
if lines.is_a? String
-
7
lines = lines.lines
-
end
-
8
@lines = lines.each_with_index.map { |line, lineno|
-
422
LOC.new(line, lineno + start_line.to_i) }
-
8
@code_type = code_type
-
end
-
-
# Append the given line. +lineno+ is one more than the last existing
-
# line, unless specified otherwise.
-
#
-
# @param [String] line
-
# @param [Integer?] lineno
-
# @return [String] The inserted line.
-
1
def push(line, lineno = nil)
-
if lineno.nil?
-
lineno = @lines.last.lineno + 1
-
end
-
@lines.push(LOC.new(line, lineno))
-
line
-
end
-
1
alias << push
-
-
# Filter the lines using the given block.
-
#
-
# @yield [LOC]
-
# @return [Code]
-
1
def select(&block)
-
alter do
-
@lines = @lines.select(&block)
-
end
-
end
-
-
# Remove all lines that aren't in the given range, expressed either as a
-
# `Range` object or a first and last line number (inclusive). Negative
-
# indices count from the end of the array of lines.
-
#
-
# @param [Range, Integer] start_line
-
# @param [Integer?] end_line
-
# @return [Code]
-
1
def between(start_line, end_line = nil)
-
return self unless start_line
-
-
code_range = CodeRange.new(start_line, end_line)
-
-
alter do
-
@lines = @lines[code_range.indices_range(@lines)] || []
-
end
-
end
-
-
# Take `num_lines` from `start_line`, forward or backwards.
-
#
-
# @param [Integer] start_line
-
# @param [Integer] num_lines
-
# @return [Code]
-
1
def take_lines(start_line, num_lines)
-
start_idx =
-
if start_line >= 0
-
@lines.index { |loc| loc.lineno >= start_line } || @lines.length
-
else
-
[@lines.length + start_line, 0].max
-
end
-
-
alter do
-
@lines = @lines.slice(start_idx, num_lines)
-
end
-
end
-
-
# Remove all lines except for the +lines+ up to and excluding +lineno+.
-
#
-
# @param [Integer] lineno
-
# @param [Integer] lines
-
# @return [Code]
-
1
def before(lineno, lines = 1)
-
return self unless lineno
-
-
select do |loc|
-
loc.lineno >= lineno - lines && loc.lineno < lineno
-
end
-
end
-
-
# Remove all lines except for the +lines+ on either side of and including
-
# +lineno+.
-
#
-
# @param [Integer] lineno
-
# @param [Integer] lines
-
# @return [Code]
-
1
def around(lineno, lines = 1)
-
return self unless lineno
-
-
select do |loc|
-
loc.lineno >= lineno - lines && loc.lineno <= lineno + lines
-
end
-
end
-
-
# Remove all lines except for the +lines+ after and excluding +lineno+.
-
#
-
# @param [Integer] lineno
-
# @param [Integer] lines
-
# @return [Code]
-
1
def after(lineno, lines = 1)
-
return self unless lineno
-
-
select do |loc|
-
loc.lineno > lineno && loc.lineno <= lineno + lines
-
end
-
end
-
-
# Remove all lines that don't match the given `pattern`.
-
#
-
# @param [Regexp] pattern
-
# @return [Code]
-
1
def grep(pattern)
-
return self unless pattern
-
pattern = Regexp.new(pattern)
-
-
select do |loc|
-
loc.line =~ pattern
-
end
-
end
-
-
# Format output with line numbers next to it, unless `y_n` is falsy.
-
#
-
# @param [Boolean?] y_n
-
# @return [Code]
-
1
def with_line_numbers(y_n = true)
-
4
alter do
-
4
@with_line_numbers = y_n
-
end
-
end
-
-
# Format output with a marker next to the given +lineno+, unless +lineno+ is
-
# falsy.
-
#
-
# @param [Integer?] lineno
-
# @return [Code]
-
1
def with_marker(lineno = 1)
-
4
alter do
-
4
@with_marker = !!lineno
-
4
@marker_lineno = lineno
-
end
-
end
-
-
# Format output with the specified number of spaces in front of every line,
-
# unless `spaces` is falsy.
-
#
-
# @param [Integer?] spaces
-
# @return [Code]
-
1
def with_indentation(spaces = 0)
-
alter do
-
@with_indentation = !!spaces
-
@indentation_num = spaces
-
end
-
end
-
-
# @return [String]
-
1
def inspect
-
Object.instance_method(:to_s).bind(self).call
-
end
-
-
# @return [Integer] the number of digits in the last line.
-
1
def max_lineno_width
-
39
@lines.length > 0 ? @lines.last.lineno.to_s.length : 0
-
end
-
-
# @return [String] a formatted representation (based on the configuration of
-
# the object).
-
1
def to_s
-
print_to_output("", false)
-
end
-
-
# @return [String] a (possibly highlighted) copy of the source code.
-
1
def highlighted
-
4
print_to_output("", true)
-
end
-
-
# Writes a formatted representation (based on the configuration of the
-
# object) to the given output, which must respond to `#<<`.
-
1
def print_to_output(output, color=false)
-
4
@lines.each do |loc|
-
39
loc = loc.dup
-
39
loc.colorize(@code_type) if color
-
39
loc.add_line_number(max_lineno_width, color) if @with_line_numbers
-
39
loc.add_marker(@marker_lineno) if @with_marker
-
39
loc.indent(@indentation_num) if @with_indentation
-
39
output << loc.line
-
39
output << "\n"
-
end
-
4
output
-
end
-
-
# Get the comment that describes the expression on the given line number.
-
#
-
# @param [Integer] line_number (1-based)
-
# @return [String] the code.
-
1
def comment_describing(line_number)
-
self.class.comment_describing(raw, line_number)
-
end
-
-
# Get the multiline expression that starts on the given line number.
-
#
-
# @param [Integer] line_number (1-based)
-
# @return [String] the code.
-
1
def expression_at(line_number, consume = 0)
-
4
self.class.expression_at(raw, line_number, :consume => consume)
-
end
-
-
# Get the (approximate) Module.nesting at the give line number.
-
#
-
# @param [Integer] line_number line number starting from 1
-
# @param [Module] top_module the module in which this code exists
-
# @return [Array<Module>] a list of open modules.
-
1
def nesting_at(line_number, top_module = Object)
-
Pry::Indent.nesting_at(raw, line_number)
-
end
-
-
# Return an unformatted String of the code.
-
#
-
# @return [String]
-
1
def raw
-
4
@lines.map(&:line).join("\n") << "\n"
-
end
-
-
# Return the number of lines stored.
-
#
-
# @return [Integer]
-
1
def length
-
@lines ? @lines.length : 0
-
end
-
-
# Two `Code` objects are equal if they contain the same lines with the same
-
# numbers. Otherwise, call `to_s` and `chomp` and compare as Strings.
-
#
-
# @param [Code, Object] other
-
# @return [Boolean]
-
1
def ==(other)
-
if other.is_a?(Code)
-
other_lines = other.instance_variable_get(:@lines)
-
@lines.each_with_index.all? { |loc, i| loc == other_lines[i] }
-
else
-
to_s.chomp == other.to_s.chomp
-
end
-
end
-
-
# Forward any missing methods to the output of `#to_s`.
-
1
def method_missing(name, *args, &block)
-
to_s.send(name, *args, &block)
-
end
-
1
undef =~
-
-
1
protected
-
-
# An abstraction of the `dup.instance_eval` pattern used throughout this
-
# class.
-
1
def alter(&block)
-
16
dup.tap { |o| o.instance_eval(&block) }
-
end
-
end
-
end
-
1
class Pry
-
1
class CodeFile
-
1
DEFAULT_EXT = '.rb'
-
-
# List of all supported languages.
-
# @return [Hash]
-
1
EXTENSIONS = {
-
%w(.py) => :python,
-
%w(.js) => :javascript,
-
%w(.css) => :css,
-
%w(.xml) => :xml,
-
%w(.php) => :php,
-
%w(.html) => :html,
-
%w(.diff) => :diff,
-
%w(.java) => :java,
-
%w(.json) => :json,
-
%w(.c .h) => :c,
-
%w(.rhtml) => :rhtml,
-
%w(.yaml .yml) => :yaml,
-
%w(.cpp .hpp .cc .h cxx) => :cpp,
-
%w(.rb .ru .irbrc .gemspec .pryrc) => :ruby,
-
}
-
-
# @return [Symbol] The type of code stored in this wrapper.
-
1
attr_reader :code_type
-
-
# @param [String] filename The name of a file with code to be detected
-
# @param [Symbol] code_type The type of code the `filename` contains
-
1
def initialize(filename, code_type = type_from_filename(filename))
-
4
@filename = filename
-
4
@code_type = code_type
-
end
-
-
# @return [String] The code contained in the current `@filename`.
-
1
def code
-
4
if @filename == Pry.eval_path
-
1
Pry.line_buffer.drop(1)
-
3
elsif Pry::Method::Patcher.code_for(@filename)
-
Pry::Method::Patcher.code_for(@filename)
-
3
elsif RbxPath.is_core_path?(@filename)
-
File.read(RbxPath.convert_path_to_full(@filename))
-
else
-
3
path = abs_path
-
3
@code_type = type_from_filename(path)
-
3
File.read(path)
-
end
-
end
-
-
1
private
-
-
# @raise [MethodSource::SourceNotFoundError] if the `filename` is not
-
# readable for some reason.
-
# @return [String] absolute path for the given `filename`.
-
1
def abs_path
-
3
code_path.detect { |path| readable?(path) } or
-
raise MethodSource::SourceNotFoundError,
-
3
"Cannot open #{ @filename.inspect } for reading."
-
end
-
-
# @param [String] path
-
# @return [Boolean] if the path, with or without the default ext,
-
# is a readable file then `true`, otherwise `false`.
-
1
def readable?(path)
-
File.readable?(path) && !File.directory?(path) or
-
3
File.readable?(path << DEFAULT_EXT)
-
end
-
-
# @return [Array] All the paths that contain code that Pry can use for its
-
# API's. Skips directories.
-
1
def code_path
-
3
[from_pwd, from_pry_init_pwd, *from_load_path]
-
end
-
-
# @param [String] filename
-
# @param [Symbol] default (:unknown) the file type to assume if none could be
-
# detected.
-
# @return [Symbol, nil] The CodeRay type of a file from its extension, or
-
# `nil` if `:unknown`.
-
1
def type_from_filename(filename, default = :unknown)
-
3
_, @code_type = EXTENSIONS.find do |k, _|
-
102
k.any? { |ext| ext == File.extname(filename) }
-
end
-
-
3
code_type || default
-
end
-
-
# @return [String]
-
1
def from_pwd
-
3
File.expand_path(@filename, Dir.pwd)
-
end
-
-
# @return [String]
-
1
def from_pry_init_pwd
-
3
File.expand_path(@filename, Pry::INITIAL_PWD)
-
end
-
-
# @return [String]
-
1
def from_load_path
-
99
$LOAD_PATH.map { |path| File.expand_path(@filename, path) }
-
end
-
-
end
-
end
-
1
class Pry
-
1
class Code
-
-
# Represents a range of lines in a code listing.
-
#
-
# @api private
-
1
class CodeRange
-
-
# @param [Integer] start_line
-
# @param [Integer?] end_line
-
1
def initialize(start_line, end_line = nil)
-
@start_line = start_line
-
@end_line = end_line
-
force_set_end_line
-
end
-
-
# @param [Array<LOC>] lines
-
# @return [Range]
-
1
def indices_range(lines)
-
Range.new(*indices(lines))
-
end
-
-
1
private
-
-
1
def start_line; @start_line; end
-
1
def end_line; @end_line; end
-
-
# If `end_line` is equal to `nil`, then calculate it from the first
-
# parameter, `start_line`. Otherwise, leave it as it is.
-
# @return [void]
-
1
def force_set_end_line
-
if start_line.is_a?(Range)
-
set_end_line_from_range
-
else
-
@end_line ||= start_line
-
end
-
end
-
-
# Finds indices of `start_line` and `end_line` in the given Array of
-
# +lines+.
-
#
-
# @param [Array<LOC>] lines
-
# @return [Array<Integer>]
-
1
def indices(lines)
-
[find_start_index(lines), find_end_index(lines)]
-
end
-
-
# @return [Integer]
-
1
def find_start_index(lines)
-
return start_line if start_line < 0
-
lines.index { |loc| loc.lineno >= start_line } || lines.length
-
end
-
-
# @return [Integer]
-
1
def find_end_index(lines)
-
return end_line if end_line < 0
-
(lines.index { |loc| loc.lineno > end_line } || 0) - 1
-
end
-
-
# For example, if the range is 4..10, then `start_line` would be equal to
-
# 4 and `end_line` to 10.
-
# @return [void]
-
1
def set_end_line_from_range
-
@end_line = start_line.last
-
@end_line -= 1 if start_line.exclude_end?
-
@start_line = start_line.first
-
end
-
end
-
-
end
-
end
-
1
class Pry
-
1
class Code
-
-
# Represents a line of code. A line of code is a tuple, which consists of a
-
# line and a line number. A `LOC` object's state (namely, the line
-
# parameter) can be changed via instance methods. `Pry::Code` heavily uses
-
# this class.
-
#
-
# @api private
-
# @example
-
# loc = LOC.new("def example\n :example\nend", 1)
-
# puts loc.line
-
# def example
-
# :example
-
# end
-
# #=> nil
-
#
-
# loc.indent(3)
-
# loc.line #=> " def example\n :example\nend"
-
1
class LOC
-
-
# @return [Array<String, Integer>]
-
1
attr_reader :tuple
-
-
# @param [String] line The line of code.
-
# @param [Integer] lineno The position of the +line+.
-
1
def initialize(line, lineno)
-
461
@tuple = [line.chomp, lineno.to_i]
-
end
-
-
# @return [Boolean]
-
1
def ==(other)
-
other.tuple == tuple
-
end
-
-
1
def dup
-
39
self.class.new(line, lineno)
-
end
-
-
# @return [String]
-
1
def line
-
578
tuple.first
-
end
-
-
# @return [Integer]
-
1
def lineno
-
156
tuple.last
-
end
-
-
# Paints the `line` of code.
-
#
-
# @param [Symbol] code_type
-
# @return [void]
-
1
def colorize(code_type)
-
39
tuple[0] = CodeRay.scan(line, code_type).term
-
end
-
-
# Prepends the line number `lineno` to the `line`.
-
#
-
# @param [Integer] max_width
-
# @return [void]
-
1
def add_line_number(max_width = 0, color = false)
-
39
padded = lineno.to_s.rjust(max_width)
-
39
colorized_lineno = color ? Pry::Helpers::BaseHelpers.colorize_code(padded) : padded
-
39
tuple[0] = "#{ colorized_lineno }: #{ line }"
-
end
-
-
# Prepends a marker "=>" or an empty marker to the +line+.
-
#
-
# @param [Integer] marker_lineno If it is equal to the `lineno`, then
-
# prepend a hashrocket. Otherwise, an empty marker.
-
# @return [void]
-
1
def add_marker(marker_lineno)
-
39
tuple[0] =
-
if lineno == marker_lineno
-
4
" => #{ line }"
-
else
-
35
" #{ line }"
-
end
-
end
-
-
# Indents the `line` with +distance+ spaces.
-
#
-
# @param [Integer] distance
-
# @return [void]
-
1
def indent(distance)
-
tuple[0] = "#{ ' ' * distance }#{ line }"
-
end
-
end
-
-
end
-
end
-
1
class Pry
-
-
# This class is responsible for taking a string (identifying a
-
# command/class/method/etc) and returning the relevant type of object.
-
# For example, if the user looks up "show-source" then a
-
# `Pry::Command` will be returned. Alternatively, if the user passes in "Pry#repl" then
-
# a `Pry::Method` object will be returned.
-
#
-
# The `CodeObject.lookup` method is responsible for 1. figuring out what kind of
-
# object the user wants (applying precedence rules in doing so -- i.e methods
-
# get precedence over commands with the same name) and 2. Returning
-
# the appropriate object. If the user fails to provide a string
-
# identifer for the object (i.e they pass in `nil` or "") then the
-
# object looked up will be the 'current method' or 'current class'
-
# associated with the Binding.
-
#
-
# TODO: This class is a clusterfuck. We need a much more robust
-
# concept of what a "Code Object" really is. Currently
-
# commands/classes/candidates/methods and so on just share a very
-
# ill-defined interface.
-
1
class CodeObject
-
1
module Helpers
-
# we need this helper as some Pry::Method objects can wrap Procs
-
# @return [Boolean]
-
1
def real_method_object?
-
is_a?(::Method) || is_a?(::UnboundMethod)
-
end
-
-
1
def c_method?
-
real_method_object? && source_type == :c
-
end
-
-
1
def module_with_yard_docs?
-
is_a?(WrappedModule) && yard_docs?
-
end
-
-
1
def command?
-
is_a?(Module) && self <= Pry::Command
-
end
-
end
-
-
1
include Pry::Helpers::CommandHelpers
-
-
1
class << self
-
1
def lookup(str, _pry_, options={})
-
co = new(str, _pry_, options)
-
-
co.default_lookup || co.method_or_class_lookup ||
-
co.command_lookup || co.empty_lookup
-
end
-
end
-
-
1
attr_accessor :str
-
1
attr_accessor :target
-
1
attr_accessor :_pry_
-
1
attr_accessor :super_level
-
-
1
def initialize(str, _pry_, options={})
-
options = {
-
:super => 0,
-
}.merge!(options)
-
-
@str = str
-
@_pry_ = _pry_
-
@target = _pry_.current_context
-
@super_level = options[:super]
-
end
-
-
1
def command_lookup
-
# TODO: just make it so find_command_by_match_or_listing doesn't
-
# raise?
-
_pry_.commands.find_command_by_match_or_listing(str) rescue nil
-
end
-
-
# when no paramter is given (i.e CodeObject.lookup(nil)), then we
-
# lookup the 'current object' from the binding.
-
1
def empty_lookup
-
return nil if str && !str.empty?
-
-
obj = if internal_binding?(target)
-
mod = target_self.is_a?(Module) ? target_self : target_self.class
-
Pry::WrappedModule(mod)
-
else
-
Pry::Method.from_binding(target)
-
end
-
-
# respect the super level (i.e user might have specified a
-
# --super flag to show-source)
-
lookup_super(obj, super_level)
-
end
-
-
# lookup variables and constants and `self` that are not modules
-
1
def default_lookup
-
-
# we skip instance methods as we want those to fall through to method_or_class_lookup()
-
if safe_to_evaluate?(str) && !looks_like_an_instance_method?(str)
-
obj = target.eval(str)
-
-
# restrict to only objects we KNOW for sure support the full API
-
# Do NOT support just any object that responds to source_location
-
if sourcable_object?(obj)
-
Pry::Method(obj)
-
elsif !obj.is_a?(Module)
-
Pry::WrappedModule(obj.class)
-
else
-
nil
-
end
-
end
-
-
rescue Pry::RescuableException
-
nil
-
end
-
-
1
def method_or_class_lookup
-
obj = case str
-
when /\S+\(\)\z/
-
Pry::Method.from_str(str.sub(/\(\)\z/, ''),target) || Pry::WrappedModule.from_str(str, target)
-
else
-
Pry::WrappedModule.from_str(str,target) || Pry::Method.from_str(str, target)
-
end
-
-
lookup_super(obj, super_level)
-
end
-
-
1
private
-
-
1
def sourcable_object?(obj)
-
[::Proc, ::Method, ::UnboundMethod].any? { |o| obj.is_a?(o) }
-
end
-
-
# Returns true if `str` looks like a method, i.e Klass#method
-
# We need to consider this case because method lookups should fall
-
# through to the `method_or_class_lookup()` method but a
-
# defined?() on a "Klass#method` string will see the `#` as a
-
# comment and only evaluate the `Klass` part.
-
# @param [String] str
-
# @return [Boolean] Whether the string looks like an instance method.
-
1
def looks_like_an_instance_method?(str)
-
str =~ /\S#\S/
-
end
-
-
# We use this method to decide whether code is safe to eval. Method's are
-
# generally not, but everything else is.
-
# TODO: is just checking != "method" enough??
-
# TODO: see duplication of this method in Pry::WrappedModule
-
# @param [String] str The string to lookup
-
# @return [Boolean]
-
1
def safe_to_evaluate?(str)
-
return true if str.strip == "self"
-
kind = target.eval("defined?(#{str})")
-
kind =~ /variable|constant/
-
end
-
-
1
def target_self
-
target.eval('self')
-
end
-
-
# grab the nth (`super_level`) super of `obj
-
# @param [Object] obj
-
# @param [Fixnum] super_level How far up the super chain to ascend.
-
1
def lookup_super(obj, super_level)
-
return nil if !obj
-
-
sup = obj.super(super_level)
-
if !sup
-
raise Pry::CommandError, "No superclass found for #{obj.wrapped}"
-
else
-
sup
-
end
-
end
-
end
-
end
-
# PP subclass for streaming inspect output in color.
-
1
class Pry
-
1
class ColorPrinter < ::PP
-
1
OBJ_COLOR = begin
-
1
code = CodeRay::Encoders::Terminal::TOKEN_COLORS[:keyword]
-
1
if code.start_with? "\e"
-
1
code
-
else
-
"\e[0m\e[0;#{code}m"
-
end
-
end
-
-
1
CodeRay::Encoders::Terminal::TOKEN_COLORS[:comment][:self] = "\e[1;34m"
-
-
1
def self.pp(obj, out = $>, width = 79)
-
1
q = ColorPrinter.new(out, width)
-
2
q.guard_inspect_key { q.pp obj }
-
1
q.flush
-
1
out << "\n"
-
end
-
-
1
def text(str, width = str.length)
-
# Don't recolorize output with color [Issue #751]
-
3
if str.include?("\e[")
-
super "#{str}\e[0m", width
-
3
elsif str.start_with?('#<') || str == '=' || str == '>'
-
super highlight_object_literal(str), width
-
else
-
3
super CodeRay.scan(str, :ruby).term, width
-
end
-
end
-
-
1
def pp(obj)
-
1
super
-
rescue => e
-
raise if e.is_a? Pry::Pager::StopPaging
-
-
# Read the class name off of the singleton class to provide a default
-
# inspect.
-
singleton = class << obj; self; end
-
ancestors = Pry::Method.safe_send(singleton, :ancestors)
-
klass = ancestors.reject { |k| k == singleton }.first
-
obj_id = obj.__id__.to_s(16) rescue 0
-
str = "#<#{klass}:0x#{obj_id}>"
-
-
text highlight_object_literal(str)
-
end
-
-
1
private
-
-
1
def highlight_object_literal(object_literal)
-
"#{OBJ_COLOR}#{object_literal}\e[0m"
-
end
-
end
-
end
-
1
require 'delegate'
-
1
require 'pry/helpers/documentation_helpers'
-
-
1
class Pry
-
-
# The super-class of all commands, new commands should be created by calling
-
# {Pry::CommandSet#command} which creates a BlockCommand or {Pry::CommandSet#create_command}
-
# which creates a ClassCommand. Please don't use this class directly.
-
1
class Command
-
1
extend Helpers::DocumentationHelpers
-
1
extend CodeObject::Helpers
-
-
# represents a void return value for a command
-
1
VOID_VALUE = Object.new
-
-
# give it a nice inspect
-
1
def VOID_VALUE.inspect() "void" end
-
-
# Properties of the command itself (as passed as arguments to
-
# {CommandSet#command} or {CommandSet#create_command}).
-
1
class << self
-
1
attr_writer :block
-
1
attr_writer :description
-
1
attr_writer :command_options
-
1
attr_writer :match
-
-
1
def match(arg=nil)
-
2722
if arg
-
50
@command_options ||= default_options(arg)
-
50
@command_options[:listing] = arg.is_a?(String) ? arg : arg.inspect
-
50
@match = arg
-
end
-
2722
@match ||= nil
-
end
-
-
# Define or get the command's description
-
1
def description(arg=nil)
-
152
@description = arg if arg
-
152
@description ||= nil
-
end
-
-
# Define or get the command's options
-
1
def command_options(arg=nil)
-
2576
@command_options ||= default_options(match)
-
2576
@command_options.merge!(arg) if arg
-
2576
@command_options
-
end
-
# backward compatibility
-
1
alias_method :options, :command_options
-
1
alias_method :options=, :command_options=
-
-
# Define or get the command's banner
-
1
def banner(arg=nil)
-
52
@banner = arg if arg
-
52
@banner || description
-
end
-
-
1
def block
-
@block || instance_method(:process)
-
end
-
-
1
def source
-
file, line = block.source_location
-
strip_leading_whitespace(Pry::Code.from_file(file).expression_at(line))
-
end
-
-
1
def doc
-
new.help
-
end
-
-
1
def source_location
-
block.source_location
-
end
-
-
1
def source_file
-
Array(block.source_location).first
-
end
-
1
alias_method :file, :source_file
-
-
1
def source_line
-
Array(block.source_location).last
-
end
-
1
alias_method :line, :source_line
-
-
1
def default_options(match)
-
{
-
:requires_gem => [],
-
:keep_retval => false,
-
:argument_required => false,
-
:interpolate => true,
-
:shellwords => true,
-
73
:listing => (String === match ? match : match.inspect),
-
:use_prefix => true,
-
:takes_block => false
-
73
}
-
end
-
end
-
-
# Make those properties accessible to instances
-
1
def name; self.class.name; end
-
1
def match; self.class.match; end
-
1
def description; self.class.description; end
-
1
def block; self.class.block; end
-
24
def command_options; self.class.options; end
-
1
def command_name; self.class.command_name; end
-
1
def source; self.class.source; end
-
1
def source_location; self.class.source_location; end
-
-
1
class << self
-
1
def name
-
super.to_s == "" ? "#<class(Pry::Command #{match.inspect})>" : super
-
end
-
-
1
def inspect
-
name
-
end
-
-
1
def command_name
-
self.options[:listing]
-
end
-
-
# Create a new command with the given properties.
-
# @param [String, Regex] match The thing that triggers this command
-
# @param [String] description The description to appear in `help`
-
# @param [Hash] options Behavioral options (see {Pry::CommandSet#command})
-
# @param [Module] helpers A module of helper functions to be included.
-
# @yield optional, used for BlockCommands
-
# @return [Class] (a subclass of {Pry::Command})
-
1
def subclass(match, description, options, helpers, &block)
-
21
klass = Class.new(self)
-
21
klass.send(:include, helpers)
-
21
klass.match = match
-
21
klass.description = description
-
21
klass.command_options = options
-
21
klass.block = block
-
21
klass
-
end
-
-
# Should this command be called for the given line?
-
# @param [String] val A line input at the REPL
-
# @return [Boolean]
-
1
def matches?(val)
-
2402
command_regex =~ val
-
end
-
-
# How well does this command match the given line?
-
#
-
# Higher scores are better because they imply that this command matches
-
# the line more closely.
-
#
-
# The score is calculated by taking the number of characters at the start
-
# of the string that are used only to identify the command, not as part of
-
# the arguments.
-
#
-
# @example
-
# /\.(.*)/.match_score(".foo") #=> 1
-
# /\.*(.*)/.match_score("...foo") #=> 3
-
# 'hi'.match_score("hi there") #=> 2
-
#
-
# @param [String] val A line input at the REPL
-
# @return [Fixnum]
-
1
def match_score(val)
-
16
if command_regex =~ val
-
16
Regexp.last_match.size > 1 ? Regexp.last_match.begin(1) : Regexp.last_match.end(0)
-
else
-
-1
-
end
-
end
-
-
# Store hooks to be run before or after the command body.
-
# @see {Pry::CommandSet#before_command}
-
# @see {Pry::CommandSet#after_command}
-
1
def hooks
-
7
@hooks ||= {:before => [], :after => []}
-
end
-
-
1
def command_regex
-
2422
pr = Pry.respond_to?(:config) ? Pry.config.command_prefix : ""
-
2422
prefix = convert_to_regex(pr)
-
2422
prefix = "(?:#{prefix})?" unless options[:use_prefix]
-
-
2422
/^#{prefix}#{convert_to_regex(match)}(?!\S)/
-
end
-
-
1
def convert_to_regex(obj)
-
4844
case obj
-
when String
-
4626
Regexp.escape(obj)
-
else
-
218
obj
-
end
-
end
-
-
# The group in which the command should be displayed in "help" output.
-
# This is usually auto-generated from directory naming, but it can be
-
# manually overridden if necessary.
-
# Group should not be changed once it is initialized.
-
1
def group(name=nil)
-
@group ||= if name
-
60
name
-
else
-
case Pry::Method(block).source_file
-
when %r{/pry/.*_commands/(.*).rb}
-
$1.capitalize.gsub(/_/, " ")
-
when %r{(pry-\w+)-([\d\.]+([\w\.]+)?)}
-
name, version = $1, $2
-
"#{name.to_s} (v#{version.to_s})"
-
when /pryrc/
-
"~/.pryrc"
-
else
-
"(other)"
-
end
-
60
end
-
end
-
end
-
-
# Properties of one execution of a command (passed by {Pry#run_command} as a hash of
-
# context and expanded in `#initialize`
-
1
attr_accessor :output
-
1
attr_accessor :target
-
1
attr_accessor :captures
-
1
attr_accessor :eval_string
-
1
attr_accessor :arg_string
-
1
attr_accessor :context
-
1
attr_accessor :command_set
-
1
attr_accessor :_pry_
-
-
# The block we pass *into* a command so long as `:takes_block` is
-
# not equal to `false`
-
# @example
-
# my-command | do
-
# puts "block content"
-
# end
-
1
attr_accessor :command_block
-
-
# Run a command from another command.
-
# @param [String] command_string The string that invokes the command
-
# @param [Array] args Further arguments to pass to the command
-
# @example
-
# run "show-input"
-
# @example
-
# run ".ls"
-
# @example
-
# run "amend-line", "5", 'puts "hello world"'
-
1
def run(command_string, *args)
-
command_string = _pry_.config.command_prefix.to_s + command_string
-
complete_string = "#{command_string} #{args.join(" ")}".rstrip
-
command_set.process_line(complete_string, context)
-
end
-
-
1
def commands
-
command_set.to_hash
-
end
-
-
1
def text
-
4
Pry::Helpers::Text
-
end
-
-
1
def void
-
3
VOID_VALUE
-
end
-
-
1
include Pry::Helpers::BaseHelpers
-
1
include Pry::Helpers::CommandHelpers
-
-
# Instantiate a command, in preparation for calling it.
-
# @param [Hash] context The runtime context to use with this command.
-
1
def initialize(context={})
-
4
self.context = context
-
4
self.target = context[:target]
-
4
self.output = context[:output]
-
4
self.eval_string = context[:eval_string]
-
4
self.command_set = context[:command_set]
-
4
self._pry_ = context[:pry_instance]
-
end
-
-
# @return [Object] The value of `self` inside the `target` binding.
-
1
def target_self; target.eval('self'); end
-
-
# @return [Hash] Pry commands can store arbitrary state
-
# here. This state persists between subsequent command invocations.
-
# All state saved here is unique to the command, it does not
-
# need to be namespaced.
-
# @example
-
# state.my_state = "my state" # this will not conflict with any
-
# # `state.my_state` used in another command.
-
1
def state
-
_pry_.command_state[match] ||= Pry::Config.from_hash({})
-
end
-
-
# Revaluate the string (str) and perform interpolation.
-
# @param [String] str The string to reevaluate with interpolation.
-
#
-
# @return [String] The reevaluated string with interpolations
-
# applied (if any).
-
1
def interpolate_string(str)
-
4
dumped_str = str.dump
-
4
if dumped_str.gsub!(/\\\#\{/, '#{')
-
target.eval(dumped_str)
-
else
-
4
str
-
end
-
end
-
-
# Display a warning if a command collides with a local/method in
-
# the current scope.
-
1
def check_for_command_collision(command_match, arg_string)
-
collision_type = target.eval("defined?(#{command_match})")
-
collision_type ||= 'local-variable' if arg_string.match(%r{\A\s*[-+*/%&|^]*=})
-
-
if collision_type
-
output.puts "#{text.bold('WARNING:')} Calling Pry command '#{command_match}', which conflicts with a #{collision_type}.\n\n"
-
end
-
rescue Pry::RescuableException
-
end
-
-
# Extract necessary information from a line that Command.matches? this
-
# command.
-
#
-
# Returns an array of four elements:
-
#
-
# ```
-
# [String] the portion of the line that matched with the Command match
-
# [String] a string of all the arguments (i.e. everything but the match)
-
# [Array] the captures caught by the command_regex
-
# [Array] the arguments obtained by splitting the arg_string
-
# ```
-
#
-
# @param [String] val The line of input
-
# @return [Array]
-
1
def tokenize(val)
-
4
val.replace(interpolate_string(val)) if command_options[:interpolate]
-
-
4
self.class.command_regex =~ val
-
-
# please call Command.matches? before Command#call_safely
-
4
raise CommandError, "fatal: called a command which didn't match?!" unless Regexp.last_match
-
4
captures = Regexp.last_match.captures
-
4
pos = Regexp.last_match.end(0)
-
-
4
arg_string = val[pos..-1]
-
-
# remove the one leading space if it exists
-
4
arg_string.slice!(0) if arg_string.start_with?(" ")
-
-
# process and pass a block if one is found
-
4
pass_block(arg_string) if command_options[:takes_block]
-
-
4
if arg_string
-
4
args = command_options[:shellwords] ? Shellwords.shellwords(arg_string) : arg_string.split(" ")
-
else
-
args = []
-
end
-
-
4
[val[0..pos].rstrip, arg_string, captures, args]
-
end
-
-
# Process a line that Command.matches? this command.
-
# @param [String] line The line to process
-
# @return [Object, Command::VOID_VALUE]
-
1
def process_line(line)
-
4
command_match, arg_string, captures, args = tokenize(line)
-
-
4
check_for_command_collision(command_match, arg_string) if Pry.config.collision_warning
-
-
4
self.arg_string = arg_string
-
4
self.captures = captures
-
-
4
call_safely(*(captures + args))
-
end
-
-
# Pass a block argument to a command.
-
# @param [String] arg_string The arguments (as a string) passed to the command.
-
# We inspect these for a '| do' or a '| {' and if we find it we use it
-
# to start a block input sequence. Once we have a complete
-
# block, we save it to an accessor that can be retrieved from the command context.
-
# Note that if we find the '| do' or '| {' we delete this and the
-
# elements following it from `arg_string`.
-
1
def pass_block(arg_string)
-
# Workaround for weird JRuby bug where rindex in this case can return nil
-
# even when there's a match.
-
arg_string.scan(/\| *(?:do|\{)/)
-
block_index = $~ && $~.offset(0)[0]
-
-
return if !block_index
-
-
block_init_string = arg_string.slice!(block_index..-1)[1..-1]
-
prime_string = "proc #{block_init_string}\n"
-
-
if !Pry::Code.complete_expression?(prime_string)
-
block_string = _pry_.r(target, prime_string)
-
else
-
block_string = prime_string
-
end
-
-
begin
-
self.command_block = target.eval(block_string)
-
rescue Pry::RescuableException
-
raise CommandError, "Incomplete block definition."
-
end
-
end
-
-
1
private :pass_block
-
-
# Run the command with the given `args`.
-
#
-
# This is a public wrapper around `#call` which ensures all preconditions
-
# are met.
-
#
-
# @param [Array<String>] args The arguments to pass to this command.
-
# @return [Object] The return value of the `#call` method, or
-
# {Command::VOID_VALUE}.
-
1
def call_safely(*args)
-
4
unless dependencies_met?
-
gems_needed = Array(command_options[:requires_gem])
-
gems_not_installed = gems_needed.select { |g| !Rubygem.installed?(g) }
-
output.puts "\nThe command '#{command_name}' is #{text.bold("unavailable")} because it requires the following gems to be installed: #{(gems_not_installed.join(", "))}"
-
output.puts "-"
-
output.puts "Type `install-command #{command_name}` to install the required gems and activate this command."
-
return void
-
end
-
-
4
if command_options[:argument_required] && args.empty?
-
raise CommandError, "The command '#{command_name}' requires an argument."
-
end
-
-
4
ret = call_with_hooks(*args)
-
3
command_options[:keep_retval] ? ret : void
-
end
-
-
# Are all the gems required to use this command installed?
-
#
-
# @return Boolean
-
1
def dependencies_met?
-
4
@dependencies_met ||= command_dependencies_met?(command_options)
-
end
-
-
# Generate completions for this command
-
#
-
# @param [String] search The line typed so far
-
# @return [Array<String>] Completion words
-
1
def complete(search)
-
[]
-
end
-
-
1
private
-
-
# Run the `#call` method and all the registered hooks.
-
# @param [Array<String>] args The arguments to `#call`
-
# @return [Object] The return value from `#call`
-
1
def call_with_hooks(*args)
-
4
self.class.hooks[:before].each do |block|
-
instance_exec(*args, &block)
-
end
-
-
4
ret = call(*args)
-
-
3
self.class.hooks[:after].each do |block|
-
ret = instance_exec(*args, &block)
-
end
-
-
3
ret
-
end
-
-
# Fix the number of arguments we pass to a block to avoid arity warnings.
-
# @param [Fixnum] arity The arity of the block
-
# @param [Array] args The arguments to pass
-
# @return [Array] A (possibly shorter) array of the arguments to pass
-
1
def correct_arg_arity(arity, args)
-
case
-
when arity < 0
-
args
-
when arity == 0
-
4
[]
-
when arity > 0
-
args.values_at(*(0..(arity - 1)).to_a)
-
4
end
-
end
-
end
-
-
# A super-class for Commands that are created with a single block.
-
#
-
# This class ensures that the block is called with the correct number of arguments
-
# and the right context.
-
#
-
# Create subclasses using {Pry::CommandSet#command}.
-
1
class BlockCommand < Command
-
# backwards compatibility
-
1
alias_method :opts, :context
-
-
# Call the block that was registered with this command.
-
# @param [Array<String>] args The arguments passed
-
# @return [Object] The return value of the block
-
1
def call(*args)
-
instance_exec(*correct_arg_arity(block.arity, args), &block)
-
end
-
-
1
def help
-
"#{command_options[:listing].to_s.ljust(18)} #{description}"
-
end
-
end
-
-
# A super-class of Commands with structure.
-
#
-
# This class implements the bare-minimum functionality that a command should
-
# have, namely a --help switch, and then delegates actual processing to its
-
# subclasses.
-
#
-
# Create subclasses using {Pry::CommandSet#create_command}, and override the
-
# `options(opt)` method to set up an instance of Slop, and the `process`
-
# method to actually run the command. If necessary, you can also override
-
# `setup` which will be called before `options`, for example to require any
-
# gems your command needs to run, or to set up state.
-
1
class ClassCommand < Command
-
1
class << self
-
-
# Ensure that subclasses inherit the options, description and
-
# match from a ClassCommand super class.
-
1
def inherited(klass)
-
51
klass.match match
-
51
klass.description description
-
51
klass.command_options options
-
end
-
-
1
def source
-
source_object.source
-
end
-
-
1
def doc
-
new.help
-
end
-
-
1
def source_location
-
source_object.source_location
-
end
-
-
1
def source_file
-
source_object.source_file
-
end
-
1
alias_method :file, :source_file
-
-
1
def source_line
-
source_object.source_line
-
end
-
1
alias_method :line, :source_line
-
-
1
private
-
-
# The object used to extract the source for the command.
-
#
-
# This should be a `Pry::Method(block)` for a command made with `create_command`
-
# and a `Pry::WrappedModule(self)` for a command that's a standard class.
-
# @return [Pry::WrappedModule, Pry::Method]
-
1
def source_object
-
@source_object ||= if name =~ /^[A-Z]/
-
Pry::WrappedModule(self)
-
else
-
Pry::Method(block)
-
end
-
end
-
end
-
-
1
attr_accessor :opts
-
1
attr_accessor :args
-
-
# Set up `opts` and `args`, and then call `process`.
-
#
-
# This method will display help if necessary.
-
#
-
# @param [Array<String>] args The arguments passed
-
# @return [Object] The return value of `process` or VOID_VALUE
-
1
def call(*args)
-
4
setup
-
-
4
self.opts = slop
-
4
self.args = self.opts.parse!(args)
-
-
4
if opts.present?(:help)
-
output.puts slop.help
-
void
-
else
-
4
process(*correct_arg_arity(method(:process).arity, args))
-
end
-
end
-
-
# Return the help generated by Slop for this command.
-
1
def help
-
slop.help
-
end
-
-
# Return an instance of Slop that can parse either subcommands or the
-
# options that this command accepts.
-
1
def slop
-
4
Slop.new do |opt|
-
4
opt.banner(unindent(self.class.banner))
-
4
subcommands(opt)
-
4
options(opt)
-
4
opt.on :h, :help, 'Show this message.'
-
end
-
end
-
-
# Generate shell completions
-
# @param [String] search The line typed so far
-
# @return [Array<String>] the words to complete
-
1
def complete(search)
-
slop.map do |opt|
-
[opt.long && "--#{opt.long} " || opt.short && "-#{opt.short}"]
-
end.flatten(1).compact + super
-
end
-
-
# A method called just before `options(opt)` as part of `call`.
-
#
-
# This method can be used to set up any context your command needs to run,
-
# for example requiring gems, or setting default values for options.
-
#
-
# @example
-
# def setup
-
# require 'gist'
-
# @action = :method
-
# end
-
1
def setup; end
-
-
# A method to setup Slop commands so it can parse the subcommands your
-
# command expects. If you need to set up default values, use `setup`
-
# instead.
-
#
-
# @example A minimal example
-
# def subcommands(cmd)
-
# cmd.command :download do |opt|
-
# description 'Downloads a content from a server'
-
#
-
# opt.on :verbose, 'Use verbose output'
-
#
-
# run do |options, arguments|
-
# ContentDownloader.download(options, arguments)
-
# end
-
# end
-
# end
-
#
-
# @example Define the invokation block anywhere you want
-
# def subcommands(cmd)
-
# cmd.command :download do |opt|
-
# description 'Downloads a content from a server'
-
#
-
# opt.on :verbose, 'Use verbose output'
-
# end
-
# end
-
#
-
# def process
-
# # Perform calculations...
-
# opts.fetch_command(:download).run do |options, arguments|
-
# ContentDownloader.download(options, arguments)
-
# end
-
# # More calculations...
-
# end
-
1
def subcommands(cmd); end
-
-
# A method to setup Slop so it can parse the options your command expects.
-
#
-
# @note Please don't do anything side-effecty in the main part of this
-
# method, as it may be called by Pry at any time for introspection reasons.
-
# If you need to set up default values, use `setup` instead.
-
#
-
# @example
-
# def options(opt)
-
# opt.banner "Gists methods or classes"
-
# opt.on(:c, :class, "gist a class") do
-
# @action = :class
-
# end
-
# end
-
1
def options(opt); end
-
-
# The actual body of your command should go here.
-
#
-
# The `opts` mehod can be called to get the options that Slop has passed,
-
# and `args` gives the remaining, unparsed arguments.
-
#
-
# The return value of this method is discarded unless the command was
-
# created with `:keep_retval => true`, in which case it is returned to the
-
# repl.
-
#
-
# @example
-
# def process
-
# if opts.present?(:class)
-
# gist_class
-
# else
-
# gist_method
-
# end
-
# end
-
1
def process; raise CommandError, "command '#{command_name}' not implemented" end
-
end
-
end
-
1
class Pry
-
1
class NoCommandError < StandardError
-
1
def initialize(match, owner)
-
super "Command '#{match}' not found in command set #{owner}"
-
end
-
end
-
-
# This class is used to create sets of commands. Commands can be imported from
-
# different sets, aliased, removed, etc.
-
1
class CommandSet
-
1
include Enumerable
-
1
include Pry::Helpers::BaseHelpers
-
1
attr_reader :helper_module
-
-
# @param [Array<Commandset>] imported_sets
-
# Sets which will be imported automatically
-
# @yield Optional block run to define commands
-
1
def initialize(*imported_sets, &block)
-
1
@commands = {}
-
1
@helper_module = Module.new
-
1
import(*imported_sets)
-
1
instance_eval(&block) if block
-
end
-
-
# Defines a new Pry command.
-
# @param [String, Regexp] match The start of invocations of this command.
-
# @param [String] description A description of the command.
-
# @param [Hash] options The optional configuration parameters.
-
# @option options [Boolean] :keep_retval Whether or not to use return value
-
# of the block for return of `command` or just to return `nil`
-
# (the default).
-
# @option options [Array<String>] :requires_gem Whether the command has
-
# any gem dependencies, if it does and dependencies not met then
-
# command is disabled and a stub proc giving instructions to
-
# install command is provided.
-
# @option options [Boolean] :interpolate Whether string #{} based
-
# interpolation is applied to the command arguments before
-
# executing the command. Defaults to true.
-
# @option options [String] :listing The listing name of the
-
# command. That is the name by which the command is looked up by
-
# help and by show-command. Necessary for commands with regex matches.
-
# @option options [Boolean] :use_prefix Whether the command uses
-
# `Pry.config.command_prefix` prefix (if one is defined). Defaults
-
# to true.
-
# @option options [Boolean] :shellwords Whether the command's arguments
-
# should be split using Shellwords instead of just split on spaces.
-
# Defaults to true.
-
# @yield The action to perform. The parameters in the block
-
# determines the parameters the command will receive. All
-
# parameters passed into the block will be strings. Successive
-
# command parameters are separated by whitespace at the Pry prompt.
-
# @example
-
# MyCommands = Pry::CommandSet.new do
-
# command "greet", "Greet somebody" do |name|
-
# puts "Good afternoon #{name.capitalize}!"
-
# end
-
# end
-
#
-
# # From pry:
-
# # pry(main)> _pry_.commands = MyCommands
-
# # pry(main)> greet john
-
# # Good afternoon John!
-
# # pry(main)> help greet
-
# # Greet somebody
-
# @example Regexp command
-
# MyCommands = Pry::CommandSet.new do
-
# command /number-(\d+)/, "number-N regex command", :listing => "number" do |num, name|
-
# puts "hello #{name}, nice number: #{num}"
-
# end
-
# end
-
#
-
# # From pry:
-
# # pry(main)> _pry_.commands = MyCommands
-
# # pry(main)> number-10 john
-
# # hello john, nice number: 10
-
# # pry(main)> help number
-
# # number-N regex command
-
1
def block_command(match, description="No description.", options={}, &block)
-
19
description, options = ["No description.", description] if description.is_a?(Hash)
-
19
options = Pry::Command.default_options(match).merge!(options)
-
-
19
@commands[match] = Pry::BlockCommand.subclass(match, description, options, helper_module, &block)
-
end
-
1
alias_method :command, :block_command
-
-
# Defines a new Pry command class.
-
#
-
# @param [String, Regexp] match The start of invocations of this command.
-
# @param [String] description A description of the command.
-
# @param [Hash] options The optional configuration parameters, see {#command}
-
# @yield The class body's definition.
-
#
-
# @example
-
# Pry::Commands.create_command "echo", "echo's the input", :shellwords => false do
-
# def options(opt)
-
# opt.banner "Usage: echo [-u | -d] <string to echo>"
-
# opt.on :u, :upcase, "ensure the output is all upper-case"
-
# opt.on :d, :downcase, "ensure the output is all lower-case"
-
# end
-
#
-
# def process
-
# raise Pry::CommandError, "-u and -d makes no sense" if opts.present?(:u) && opts.present?(:d)
-
# result = args.join(" ")
-
# result.downcase! if opts.present?(:downcase)
-
# result.upcase! if opts.present?(:upcase)
-
# output.puts result
-
# end
-
# end
-
#
-
1
def create_command(match, description="No description.", options={}, &block)
-
2
description, options = ["No description.", description] if description.is_a?(Hash)
-
2
options = Pry::Command.default_options(match).merge!(options)
-
-
2
@commands[match] = Pry::ClassCommand.subclass(match, description, options, helper_module, &block)
-
2
@commands[match].class_eval(&block)
-
2
@commands[match]
-
end
-
-
# Execute a block of code before a command is invoked. The block also
-
# gets access to parameters that will be passed to the command and
-
# is evaluated in the same context.
-
# @param [String, Regexp] search The match or listing of the command.
-
# @yield The block to be run before the command.
-
# @example Display parameter before invoking command
-
# Pry.config.commands.before_command("whereami") do |n|
-
# output.puts "parameter passed was #{n}"
-
# end
-
1
def before_command(search, &block)
-
cmd = find_command_by_match_or_listing(search)
-
cmd.hooks[:before].unshift block
-
end
-
-
# Execute a block of code after a command is invoked. The block also
-
# gets access to parameters that will be passed to the command and
-
# is evaluated in the same context.
-
# @param [String, Regexp] search The match or listing of the command.
-
# @yield The block to be run after the command.
-
# @example Display text 'command complete' after invoking command
-
# Pry.config.commands.after_command("whereami") do |n|
-
# output.puts "command complete!"
-
# end
-
1
def after_command(search, &block)
-
cmd = find_command_by_match_or_listing(search)
-
cmd.hooks[:after] << block
-
end
-
-
1
def each(&block)
-
@commands.each(&block)
-
end
-
-
# Removes some commands from the set
-
# @param [Array<String>] searches the matches or listings of the commands to remove
-
1
def delete(*searches)
-
searches.each do |search|
-
cmd = find_command_by_match_or_listing(search)
-
@commands.delete cmd.match
-
end
-
end
-
-
# Imports all the commands from one or more sets.
-
# @param [Array<CommandSet>] sets Command sets, all of the commands of which
-
# will be imported.
-
# @return [Pry::CommandSet] Returns the reciever (a command set).
-
1
def import(*sets)
-
1
sets.each do |set|
-
@commands.merge! set.to_hash
-
helper_module.send :include, set.helper_module
-
end
-
1
self
-
end
-
-
# Imports some commands from a set
-
# @param [CommandSet] set Set to import commands from
-
# @param [Array<String>] matches Commands to import
-
# @return [Pry::CommandSet] Returns the reciever (a command set).
-
1
def import_from(set, *matches)
-
helper_module.send :include, set.helper_module
-
matches.each do |match|
-
cmd = set.find_command_by_match_or_listing(match)
-
@commands[cmd.match] = cmd
-
end
-
self
-
end
-
-
# @param [String, Regexp] match_or_listing The match or listing of a command.
-
# of the command to retrieve.
-
# @return [Command] The command object matched.
-
1
def find_command_by_match_or_listing(match_or_listing)
-
cmd = (@commands[match_or_listing] ||
-
Pry::Helpers::BaseHelpers.find_command(match_or_listing, @commands))
-
cmd or raise ArgumentError, "Cannot find a command: '#{match_or_listing}'!"
-
end
-
-
# Aliases a command
-
# @param [String, Regex] match The match of the alias (can be a regex).
-
# @param [String] action The action to be performed (typically
-
# another command).
-
# @param [Hash] options The optional configuration parameters,
-
# accepts the same as the `command` method, but also allows the
-
# command description to be passed this way too as `:desc`
-
# @example Creating an alias for `ls -M`
-
# Pry.config.commands.alias_command "lM", "ls -M"
-
# @example Pass explicit description (overriding default).
-
# Pry.config.commands.alias_command "lM", "ls -M", :desc => "cutiepie"
-
1
def alias_command(match, action, options={})
-
12
cmd = find_command(action) or fail "Command: `#{action}` not found"
-
12
original_options = cmd.options.dup
-
-
12
options = original_options.merge!({
-
:desc => "Alias for `#{action}`",
-
:listing => match
-
}).merge!(options)
-
-
# ensure default description is used if desc is nil
-
12
desc = options.delete(:desc).to_s
-
-
12
c = block_command match, desc, options do |*args|
-
run action, *args
-
end
-
-
12
c.class_eval do
-
12
define_method(:complete) do |input|
-
cmd.new(context).complete(input)
-
end
-
end
-
-
12
c.group "Aliases"
-
-
12
c
-
end
-
-
# Rename a command. Accepts either match or listing for the search.
-
#
-
# @param [String, Regexp] new_match The new match for the command.
-
# @param [String, Regexp] search The command's current match or listing.
-
# @param [Hash] options The optional configuration parameters,
-
# accepts the same as the `command` method, but also allows the
-
# command description to be passed this way too.
-
# @example Renaming the `ls` command and changing its description.
-
# Pry.config.commands.rename "dir", "ls", :description => "DOS friendly ls"
-
1
def rename_command(new_match, search, options={})
-
cmd = find_command_by_match_or_listing(search)
-
-
options = {
-
:listing => new_match,
-
:description => cmd.description
-
}.merge!(options)
-
-
@commands[new_match] = cmd.dup
-
@commands[new_match].match = new_match
-
@commands[new_match].description = options.delete(:description)
-
@commands[new_match].options.merge!(options)
-
@commands.delete(cmd.match)
-
end
-
-
1
def disabled_command(name_of_disabled_command, message, matcher=name_of_disabled_command)
-
2
create_command name_of_disabled_command do
-
2
match matcher
-
2
description ""
-
-
2
define_method(:process) do
-
output.puts "DISABLED: #{message}"
-
end
-
end
-
end
-
-
# Sets or gets the description for a command (replacing the old
-
# description). Returns current description if no description
-
# parameter provided.
-
# @param [String, Regexp] search The command match.
-
# @param [String?] description (nil) The command description.
-
# @example Setting
-
# MyCommands = Pry::CommandSet.new do
-
# desc "help", "help description"
-
# end
-
# @example Getting
-
# Pry.config.commands.desc "amend-line"
-
1
def desc(search, description=nil)
-
cmd = find_command_by_match_or_listing(search)
-
return cmd.description if !description
-
-
cmd.description = description
-
end
-
-
# Defines helpers methods for this command sets.
-
# Those helpers are only defined in this command set.
-
#
-
# @yield A block defining helper methods
-
# @example
-
# helpers do
-
# def hello
-
# puts "Hello!"
-
# end
-
#
-
# include OtherModule
-
# end
-
1
def helpers(&block)
-
helper_module.class_eval(&block)
-
end
-
-
-
# @return [Array]
-
# The list of commands provided by the command set.
-
1
def list_commands
-
@commands.keys
-
end
-
1
alias_method :keys, :list_commands
-
-
1
def to_hash
-
@commands.dup
-
end
-
1
alias_method :to_h, :to_hash
-
-
# Find a command that matches the given line
-
# @param [String] pattern The line that might be a command invocation
-
# @return [Pry::Command, nil]
-
1
def [](pattern)
-
@commands.values.select do |command|
-
2402
command.matches?(pattern)
-
end.sort_by do |command|
-
16
command.match_score(pattern)
-
40
end.last
-
end
-
1
alias_method :find_command, :[]
-
-
#
-
# Re-assign the command found at _pattern_ with _command_.
-
#
-
# @param [Regexp, String] pattern
-
# The command to add or replace(found at _pattern_).
-
#
-
# @param [Pry::Command] command
-
# The command to add.
-
#
-
# @return [Pry::Command]
-
# Returns the new command (matched with "pattern".)
-
#
-
# @example
-
# Pry.config.commands["help"] = MyHelpCommand
-
#
-
1
def []=(pattern, command)
-
48
if command.equal?(nil)
-
return @commands.delete(pattern)
-
end
-
48
unless Class === command && command < Pry::Command
-
raise TypeError, "command is not a subclass of Pry::Command"
-
end
-
48
bind_command_to_pattern = pattern != command.match
-
48
if bind_command_to_pattern
-
command_copy = command.dup
-
command_copy.match = pattern
-
@commands[pattern] = command_copy
-
else
-
48
@commands[pattern] = command
-
end
-
end
-
-
#
-
# Add a command to set.
-
#
-
# @param [Command] command
-
# a subclass of Pry::Command.
-
#
-
1
def add_command(command)
-
48
self[command.match] = command
-
end
-
-
# Find the command that the user might be trying to refer to.
-
# @param [String] search The user's search.
-
# @return [Pry::Command?]
-
1
def find_command_for_help(search)
-
find_command(search) || (begin
-
find_command_by_match_or_listing(search)
-
rescue ArgumentError
-
nil
-
end)
-
end
-
-
# Is the given line a command invocation?
-
# @param [String] val
-
# @return [Boolean]
-
1
def valid_command?(val)
-
!!find_command(val)
-
end
-
-
# Process the given line to see whether it needs executing as a command.
-
# @param [String] val The line to execute
-
# @param [Hash] context The context to execute the commands with
-
# @return [CommandSet::Result]
-
1
def process_line(val, context={})
-
28
if command = find_command(val)
-
4
context = context.merge(:command_set => self)
-
4
retval = command.new(context).process_line(val)
-
3
Result.new(true, retval)
-
else
-
24
Result.new(false)
-
end
-
end
-
-
# @private (used for testing)
-
1
def run_command(context, match, *args)
-
command = @commands[match] or raise NoCommandError.new(match, self)
-
command.new(context).call_safely(*args)
-
end
-
-
# Generate completions for the user's search.
-
# @param [String] search The line to search for
-
# @param [Hash] context The context to create the command with
-
# @return [Array<String>]
-
1
def complete(search, context={})
-
if command = find_command(search)
-
command.new(context).complete(search)
-
else
-
@commands.keys.select do |key|
-
String === key && key.start_with?(search)
-
end.map{ |key| key + " " }
-
end
-
end
-
end
-
-
# Wraps the return result of process_commands, indicates if the
-
# result IS a command and what kind of command (e.g void)
-
1
class Result
-
1
attr_reader :retval
-
-
1
def initialize(is_command, retval = nil)
-
27
@is_command, @retval = is_command, retval
-
end
-
-
# Is the result a command?
-
# @return [Boolean]
-
1
def command?
-
24
@is_command
-
end
-
-
# Is the result a command and if it is, is it a void command?
-
# (one that does not return a value)
-
# @return [Boolean]
-
1
def void_command?
-
retval == Command::VOID_VALUE
-
end
-
end
-
end
-
# Default commands used by Pry.
-
1
Pry::Commands = Pry::CommandSet.new
-
-
1
Dir[File.expand_path('../commands', __FILE__) << '/*.rb'].each do |file|
-
52
require file
-
end
-
1
class Pry
-
1
class Command::AmendLine < Pry::ClassCommand
-
1
match(/amend-line(?: (-?\d+)(?:\.\.(-?\d+))?)?/)
-
1
group 'Editing'
-
1
description 'Amend a line of input in multi-line mode.'
-
1
command_options :interpolate => false, :listing => 'amend-line'
-
-
1
banner <<-'BANNER'
-
Amend a line of input in multi-line mode. `amend-line N`, where the N represents
-
line to replace. Can also specify a range of lines using `amend-line N..M`
-
syntax. Passing "!" as replacement content deletes the line(s) instead.
-
-
amend-line 1 puts 'new' # replace line 1
-
amend-line 1..4 ! # delete lines 1..4
-
amend-line 3 >puts 'bye' # insert before line 3
-
amend-line puts 'appended' # no line number modifies immediately preceding line
-
BANNER
-
-
1
def process
-
raise CommandError, "No input to amend." if eval_string.empty?
-
-
eval_string.replace amended_input(eval_string)
-
run "fix-indent"
-
run "show-input"
-
end
-
-
1
private
-
-
# @param [String] string The string to amend.
-
# @return [String] A new string with the amendments applied to it.
-
1
def amended_input(string)
-
input_array = eval_string.each_line.to_a
-
-
if arg_string == "!"
-
delete_from_array(input_array, line_range)
-
elsif arg_string.start_with?(">")
-
insert_into_array(input_array, line_range)
-
else
-
replace_in_array(input_array, line_range)
-
end
-
-
input_array.join
-
end
-
-
1
def delete_from_array(array, range)
-
array.slice!(range)
-
end
-
-
1
def insert_into_array(array, range)
-
insert_slot = Array(range).first
-
array.insert(insert_slot, arg_string[1..-1] << "\n")
-
end
-
-
1
def replace_in_array(array, range)
-
array[range] = arg_string + "\n"
-
end
-
-
# @return [Fixnum] The number of lines currently in `eval_string` (the input buffer).
-
1
def line_count
-
eval_string.lines.count
-
end
-
-
# Returns the (one-indexed) start and end lines given by the user.
-
# The lines in this range will be affected by the `amend-line`.
-
# Returns `nil` if no lines were specified by the user.
-
# @return [Array<Fixnum>, nil]
-
1
def start_and_end_line_number
-
start_line_number, end_line_number = args
-
end_line_number ||= start_line_number.to_i
-
-
[start_line_number.to_i, end_line_number.to_i] if start_line_number
-
end
-
-
# Takes two numbers that are 1-indexed, and returns a range (or
-
# number) that is 0-indexed. 1-indexed means the first element is
-
# indentified by 1 rather than by 0 (as is the case for Ruby arrays).
-
# @param [Fixnum] start_line_number One-indexed number.
-
# @param [Fixnum] end_line_number One-indexed number.
-
# @return [Range] The zero-indexed range.
-
1
def zero_indexed_range_from_one_indexed_numbers(start_line_number, end_line_number)
-
# FIXME: one_index_number is a horrible name for this method
-
one_index_number(start_line_number)..one_index_number(end_line_number)
-
end
-
-
# The lines (or line) that will be modified by the `amend-line`.
-
# @return [Range, Fixnum] The lines or line.
-
1
def line_range
-
start_line_number, end_line_number = start_and_end_line_number
-
if start_line_number
-
zero_indexed_range_from_one_indexed_numbers(start_line_number,
-
end_line_number)
-
else
-
line_count - 1
-
end
-
end
-
end
-
-
1
Pry::Commands.add_command(Pry::Command::AmendLine)
-
end
-
1
class Pry
-
1
class Command::Bang < Pry::ClassCommand
-
1
match(/^\s*!\s*$/)
-
1
group 'Editing'
-
1
description 'Clear the input buffer.'
-
1
command_options :use_prefix => false
-
-
1
banner <<-'BANNER'
-
Clear the input buffer. Useful if the parsing process goes wrong and you get
-
stuck in the read loop.
-
BANNER
-
-
1
def process
-
output.puts 'Input buffer cleared!'
-
eval_string.replace('')
-
end
-
end
-
-
1
Pry::Commands.add_command(Pry::Command::Bang)
-
end
-
1
class Pry
-
1
class Command::BangPry < Pry::ClassCommand
-
1
match '!pry'
-
1
group 'Navigating Pry'
-
1
description 'Start a Pry session on current self.'
-
-
1
banner <<-'BANNER'
-
Start a Pry session on current self. Also works mid multi-line expression.
-
BANNER
-
-
1
def process
-
target.pry
-
end
-
end
-
-
1
Pry::Commands.add_command(Pry::Command::BangPry)
-
end
-
1
class Pry
-
1
class Command::Cat < Pry::ClassCommand
-
1
require 'pry/commands/cat/abstract_formatter.rb'
-
1
require 'pry/commands/cat/input_expression_formatter.rb'
-
1
require 'pry/commands/cat/exception_formatter.rb'
-
1
require 'pry/commands/cat/file_formatter.rb'
-
-
1
match 'cat'
-
1
group 'Input and Output'
-
1
description "Show code from a file, Pry's input buffer, or the last exception."
-
-
1
banner <<-'BANNER'
-
Usage: cat FILE
-
cat --ex [STACK_INDEX]
-
cat --in [INPUT_INDEX_OR_RANGE]
-
-
`cat` is capable of showing part or all of a source file, the context of the
-
last exception, or an expression from Pry's input history.
-
-
`cat --ex` defaults to showing the lines surrounding the location of the last
-
exception. Invoking it more than once travels up the exception's backtrace, and
-
providing a number shows the context of the given index of the backtrace.
-
BANNER
-
-
1
def options(opt)
-
opt.on :ex, "Show the context of the last exception", :optional_argument => true, :as => Integer
-
opt.on :i, :in, "Show one or more entries from Pry's expression history", :optional_argument => true, :as => Range, :default => -5..-1
-
opt.on :s, :start, "Starting line (defaults to the first line)", :optional_argument => true, :as => Integer
-
opt.on :e, :end, "Ending line (defaults to the last line)", :optional_argument => true, :as => Integer
-
opt.on :l, :'line-numbers', "Show line numbers"
-
opt.on :t, :type, "The file type for syntax highlighting (e.g., 'ruby' or 'python')", :argument => true, :as => Symbol
-
end
-
-
1
def process
-
output = case
-
when opts.present?(:ex)
-
ExceptionFormatter.new(_pry_.last_exception, _pry_, opts).format
-
when opts.present?(:in)
-
InputExpressionFormatter.new(_pry_.input_array, opts).format
-
else
-
FileFormatter.new(args.first, _pry_, opts).format
-
end
-
-
_pry_.pager.page output
-
end
-
-
1
def complete(search)
-
super | load_path_completions
-
end
-
-
1
def load_path_completions
-
$LOAD_PATH.flat_map do |path|
-
Dir[path + '/**/*'].map { |f|
-
next if File.directory?(f)
-
f.sub!(path + '/', '')
-
}
-
end
-
end
-
end
-
-
1
Pry::Commands.add_command(Pry::Command::Cat)
-
end
-
1
class Pry
-
1
class Command::Cat
-
1
class AbstractFormatter
-
1
include Pry::Helpers::CommandHelpers
-
1
include Pry::Helpers::BaseHelpers
-
-
1
private
-
1
def decorate(content)
-
content.code_type = code_type
-
content.between(*between_lines).
-
with_line_numbers(use_line_numbers?).highlighted
-
end
-
-
1
def code_type
-
opts[:type] || :ruby
-
end
-
-
1
def use_line_numbers?
-
opts.present?(:'line-numbers') || opts.present?(:ex)
-
end
-
-
1
def between_lines
-
[opts[:start] || 1, opts[:end] || -1]
-
end
-
end
-
end
-
end
-
1
class Pry
-
1
class Command::Cat
-
1
class ExceptionFormatter < AbstractFormatter
-
1
attr_reader :ex
-
1
attr_reader :opts
-
1
attr_reader :_pry_
-
-
1
def initialize(exception, _pry_, opts)
-
@ex = exception
-
@opts = opts
-
@_pry_ = _pry_
-
end
-
-
1
def format
-
check_for_errors
-
set_file_and_dir_locals(backtrace_file, _pry_, _pry_.current_context)
-
code = decorate(Pry::Code.from_file(backtrace_file).
-
between(*start_and_end_line_for_code_window).
-
with_marker(backtrace_line))
-
"#{header}#{code}"
-
end
-
-
1
private
-
-
1
def code_window_size
-
_pry_.config.default_window_size || 5
-
end
-
-
1
def backtrace_level
-
return @backtrace_level if @backtrace_level
-
-
bl = if opts[:ex].nil?
-
ex.bt_index
-
else
-
ex.bt_index = absolute_index_number(opts[:ex], ex.backtrace.size)
-
end
-
-
increment_backtrace_level
-
@backtrace_level = bl
-
end
-
-
1
def increment_backtrace_level
-
ex.inc_bt_index
-
end
-
-
1
def backtrace_file
-
Array(ex.bt_source_location_for(backtrace_level)).first
-
end
-
-
1
def backtrace_line
-
Array(ex.bt_source_location_for(backtrace_level)).last
-
end
-
-
1
def check_for_errors
-
raise CommandError, "No exception found." unless ex
-
raise CommandError, "The given backtrace level is out of bounds." unless backtrace_file
-
end
-
-
1
def start_and_end_line_for_code_window
-
start_line = backtrace_line - code_window_size
-
start_line = 1 if start_line < 1
-
-
[start_line, backtrace_line + code_window_size]
-
end
-
-
1
def header
-
unindent %{
-
#{Helpers::Text.bold 'Exception:'} #{ex.class}: #{ex.message}
-
--
-
#{Helpers::Text.bold('From:')} #{backtrace_file} @ line #{backtrace_line} @ #{Helpers::Text.bold("level: #{backtrace_level}")} of backtrace (of #{ex.backtrace.size - 1}).
-
-
}
-
end
-
-
end
-
end
-
end
-
1
class Pry
-
1
class Command::Cat
-
1
class FileFormatter < AbstractFormatter
-
1
attr_reader :file_with_embedded_line
-
1
attr_reader :opts
-
1
attr_reader :_pry_
-
-
1
def initialize(file_with_embedded_line, _pry_, opts)
-
@file_with_embedded_line = file_with_embedded_line
-
@opts = opts
-
@_pry_ = _pry_
-
@code_from_file = Pry::Code.from_file(file_name)
-
end
-
-
1
def format
-
raise CommandError, "Must provide a filename, --in, or --ex." if !file_with_embedded_line
-
-
set_file_and_dir_locals(file_name, _pry_, _pry_.current_context)
-
decorate(@code_from_file)
-
end
-
-
1
def file_and_line
-
file_name, line_num = file_with_embedded_line.split(/:(?!\/|\\)/)
-
-
[file_name, line_num ? line_num.to_i : nil]
-
end
-
-
1
private
-
-
1
def file_name
-
file_and_line.first
-
end
-
-
1
def line_number
-
file_and_line.last
-
end
-
-
1
def code_window_size
-
_pry_.config.default_window_size || 7
-
end
-
-
1
def decorate(content)
-
line_number ? super.around(line_number, code_window_size) : super
-
end
-
-
1
def code_type
-
opts[:type] || detect_code_type_from_file(file_name)
-
end
-
-
1
def detect_code_type_from_file(file_name)
-
code_type = @code_from_file.code_type
-
-
if code_type == :unknown
-
name = File.basename(file_name).split('.', 2).first
-
case name
-
when "Rakefile", "Gemfile"
-
:ruby
-
else
-
:text
-
end
-
else
-
code_type
-
end
-
end
-
end
-
end
-
end
-
1
class Pry
-
1
class Command::Cat
-
1
class InputExpressionFormatter < AbstractFormatter
-
1
attr_accessor :input_expressions
-
1
attr_accessor :opts
-
-
1
def initialize(input_expressions, opts)
-
@input_expressions = input_expressions
-
@opts = opts
-
end
-
-
1
def format
-
raise CommandError, "No input expressions!" if numbered_input_items.length < 1
-
-
if numbered_input_items.length > 1
-
content = ""
-
numbered_input_items.each do |i, s|
-
content << "#{Helpers::Text.bold(i.to_s)}:\n" << decorate(Pry::Code(s).with_indentation(2)).to_s
-
end
-
-
content
-
else
-
decorate(Pry::Code(selected_input_items.first))
-
end
-
end
-
-
1
private
-
-
1
def selected_input_items
-
input_expressions[normalized_expression_range] || []
-
end
-
-
1
def numbered_input_items
-
@numbered_input_items ||= normalized_expression_range.zip(selected_input_items).
-
reject { |_, s| s.nil? || s == "" }
-
end
-
-
1
def normalized_expression_range
-
absolute_index_range(opts[:i], input_expressions.length)
-
end
-
end
-
end
-
end
-
1
class Pry
-
1
class Command::Cd < Pry::ClassCommand
-
1
match 'cd'
-
1
group 'Context'
-
1
description 'Move into a new context (object or scope).'
-
-
1
banner <<-'BANNER'
-
Usage: cd [OPTIONS] [--help]
-
-
Move into new context (object or scope). As in UNIX shells use `cd ..` to go
-
back, `cd /` to return to Pry top-level and `cd -` to toggle between last two
-
scopes. Complex syntax (e.g `cd ../@x/@y`) also supported.
-
-
cd @x
-
cd ..
-
cd /
-
cd -
-
-
https://github.com/pry/pry/wiki/State-navigation#wiki-Changing_scope
-
BANNER
-
-
1
def process
-
state.old_stack ||= []
-
-
if arg_string.strip == "-"
-
unless state.old_stack.empty?
-
_pry_.binding_stack, state.old_stack = state.old_stack, _pry_.binding_stack
-
end
-
else
-
stack = ObjectPath.new(arg_string, _pry_.binding_stack).resolve
-
-
if stack && stack != _pry_.binding_stack
-
state.old_stack = _pry_.binding_stack
-
_pry_.binding_stack = stack
-
end
-
end
-
end
-
end
-
-
1
Pry::Commands.add_command(Pry::Command::Cd)
-
end
-
1
class Pry::Command::ChangeInspector < Pry::ClassCommand
-
1
match 'change-inspector'
-
1
group 'Input and Output'
-
1
description 'Change the current inspector proc.'
-
1
command_options argument_required: true
-
1
banner <<-BANNER
-
Usage: change-inspector NAME
-
-
Change the proc used to print return values. See list-inspectors for a list
-
of available procs and a short description of what each one does.
-
BANNER
-
-
1
def process(inspector)
-
if inspector_map.key?(inspector)
-
_pry_.print = inspector_map[inspector][:value]
-
output.puts "Switched to the '#{inspector}' inspector!"
-
else
-
raise Pry::CommandError, "'#{inspector}' isn't a known inspector!"
-
end
-
end
-
-
1
private
-
1
def inspector_map
-
Pry::Inspector::MAP
-
end
-
1
Pry::Commands.add_command(self)
-
end
-
1
class Pry::Command::ChangePrompt < Pry::ClassCommand
-
1
match 'change-prompt'
-
1
group 'Input and Output'
-
1
description 'Change the current prompt.'
-
1
command_options argument_required: true
-
1
banner <<-BANNER
-
Usage: change-prompt NAME
-
-
Change the current prompt. See list-prompts for a list of available
-
prompts.
-
BANNER
-
-
1
def process(prompt)
-
if prompt_map.key?(prompt)
-
_pry_.prompt = prompt_map[prompt][:value]
-
else
-
raise Pry::CommandError, "'#{prompt}' isn't a known prompt!"
-
end
-
end
-
-
1
private
-
1
def prompt_map
-
Pry::Prompt::MAP
-
end
-
1
Pry::Commands.add_command(self)
-
end
-
1
class Pry
-
1
class Command::CodeCollector
-
1
include Helpers::CommandHelpers
-
-
1
attr_reader :args
-
1
attr_reader :opts
-
1
attr_reader :_pry_
-
-
# The name of the explicitly given file (if any).
-
1
attr_accessor :file
-
-
1
class << self
-
1
attr_accessor :input_expression_ranges
-
1
attr_accessor :output_result_ranges
-
end
-
-
1
@input_expression_ranges = []
-
1
@output_result_ranges = []
-
-
1
def initialize(args, opts, _pry_)
-
@args = args
-
@opts = opts
-
@_pry_ = _pry_
-
end
-
-
# Add the `--lines`, `-o`, `-i`, `-s`, `-d` options.
-
1
def self.inject_options(opt)
-
@input_expression_ranges = []
-
@output_result_ranges = []
-
-
opt.on :l, :lines, "Restrict to a subset of lines. Takes a line number or range",
-
:optional_argument => true, :as => Range, :default => 1..-1
-
opt.on :o, :out, "Select lines from Pry's output result history. Takes an index or range",
-
:optional_argument => true, :as => Range, :default => -5..-1 do |r|
-
output_result_ranges << (r || (-5..-1))
-
end
-
opt.on :i, :in, "Select lines from Pry's input expression history. Takes an index or range",
-
:optional_argument => true, :as => Range, :default => -5..-1 do |r|
-
input_expression_ranges << (r || (-5..-1))
-
end
-
opt.on :s, :super, "Select the 'super' method. Can be repeated to traverse the ancestors",
-
:as => :count
-
opt.on :d, :doc, "Select lines from the code object's documentation"
-
end
-
-
# The content (i.e code/docs) for the selected object.
-
# If the user provided a bare code object, it returns the source.
-
# If the user provided the `-i` or `-o` switches, it returns the
-
# selected input/output lines joined as a string. If the user used
-
# `-d CODE_OBJECT` it returns the docs for that code object.
-
#
-
# @return [String]
-
1
def content
-
return @content if @content
-
raise CommandError, "Only one of --out, --in, --doc and CODE_OBJECT may be specified." if bad_option_combination?
-
-
content = case
-
when opts.present?(:o)
-
pry_output_content
-
when opts.present?(:i)
-
pry_input_content
-
when opts.present?(:d)
-
code_object_doc
-
else
-
code_object_source_or_file
-
end
-
-
@content ||= restrict_to_lines(content, line_range)
-
end
-
-
# The code object
-
#
-
# @return [Pry::WrappedModule, Pry::Method, Pry::Command]
-
1
def code_object
-
Pry::CodeObject.lookup(obj_name, _pry_, :super => opts[:super])
-
end
-
-
# Given a string and a range, return the `range` lines of that
-
# string.
-
#
-
# @param [String] content
-
# @param [Range, Fixnum] range
-
# @return [String] The string restricted to the given range
-
1
def restrict_to_lines(content, range)
-
Array(content.lines.to_a[range]).join
-
end
-
-
# The selected `_pry_.output_array` as a string, as specified by
-
# the `-o` switch.
-
#
-
# @return [String]
-
1
def pry_output_content
-
pry_array_content_as_string(_pry_.output_array, self.class.output_result_ranges) do |v|
-
_pry_.config.gist.inspecter.call(v)
-
end
-
end
-
-
# The selected `_pry_.input_array` as a string, as specified by
-
# the `-i` switch.
-
#
-
# @return [String]
-
1
def pry_input_content
-
pry_array_content_as_string(_pry_.input_array, self.class.input_expression_ranges) { |v| v }
-
end
-
-
# The line range passed to `--lines`, converted to a 0-indexed range.
-
1
def line_range
-
opts.present?(:lines) ? one_index_range_or_number(opts[:lines]) : 0..-1
-
end
-
-
# Name of the object argument
-
1
def obj_name
-
@obj_name ||= args.empty? ? "" : args.join(" ")
-
end
-
-
1
private
-
-
1
def bad_option_combination?
-
[opts.present?(:in), opts.present?(:out),
-
!args.empty?].count(true) > 1
-
end
-
-
1
def pry_array_content_as_string(array, ranges, &block)
-
all = ''
-
ranges.each do |range|
-
raise CommandError, "Minimum value for range is 1, not 0." if convert_to_range(range).first == 0
-
-
ranged_array = Array(array[range]) || []
-
ranged_array.compact.each { |v| all << block.call(v) }
-
end
-
-
all
-
end
-
-
1
def code_object_doc
-
(code_object && code_object.doc) or could_not_locate(obj_name)
-
end
-
-
1
def code_object_source_or_file
-
(code_object && code_object.source) || file_content
-
end
-
-
1
def file_content
-
if File.exists?(obj_name)
-
# Set the file accessor.
-
self.file = obj_name
-
File.read(obj_name)
-
else
-
could_not_locate(obj_name)
-
end
-
end
-
-
1
def could_not_locate(name)
-
raise CommandError, "Cannot locate: #{name}!"
-
end
-
-
1
def convert_to_range(n)
-
if !n.is_a?(Range)
-
(n..n)
-
else
-
n
-
end
-
end
-
end
-
end
-
1
class Pry
-
1
class Command::DisablePry < Pry::ClassCommand
-
1
match 'disable-pry'
-
1
group 'Navigating Pry'
-
1
description 'Stops all future calls to pry and exits the current session.'
-
-
1
banner <<-'BANNER'
-
Usage: disable-pry
-
-
After this command is run any further calls to pry will immediately return `nil`
-
without interrupting the flow of your program. This is particularly useful when
-
you've debugged the problem you were having, and now wish the program to run to
-
the end.
-
-
As alternatives, consider using `exit!` to force the current Ruby process
-
to quit immediately; or using `edit-method -p` to remove the `binding.pry`
-
from the code.
-
BANNER
-
-
1
def process
-
ENV['DISABLE_PRY'] = 'true'
-
_pry_.run_command "exit"
-
end
-
end
-
-
1
Pry::Commands.add_command(Pry::Command::DisablePry)
-
end
-
1
Pry::Commands.disabled_command("edit-method", "Use `edit` instead.")
-
1
Pry::Commands.disabled_command("show-command", "Use show-source [command_name] instead.")
-
1
class Pry
-
1
Pry::Commands.instance_eval do
-
1
command "nyan-cat", "", :requires_gem => ["nyancat"] do
-
run ".nyancat"
-
end
-
-
1
command(/!s\/(.*?)\/(.*?)/, "") do |source, dest|
-
eval_string.gsub!(/#{source}/) { dest }
-
run "show-input"
-
end
-
-
1
command "get-naked", "" do
-
text = %{
-
--
-
We dont have to take our clothes off to have a good time.
-
We could dance & party all night And drink some cherry wine.
-
-- Jermaine Stewart }
-
output.puts text
-
text
-
end
-
-
1
command "east-coker", "" do
-
text = %{
-
--
-
Now the light falls
-
Across the open field, leaving the deep lane
-
Shuttered with branches, dark in the afternoon,
-
Where you lean against a bank while a van passes,
-
And the deep lane insists on the direction
-
Into the village, in the electric heat
-
Hypnotised. In a warm haze the sultry light
-
Is absorbed, not refracted, by grey stone.
-
The dahlias sleep in the empty silence.
-
Wait for the early owl.
-
-- T.S Eliot
-
}
-
output.puts text
-
text
-
end
-
-
1
command "cohen-poem", "" do
-
text = %{
-
--
-
When this American woman,
-
whose thighs are bound in casual red cloth,
-
comes thundering past my sitting place
-
like a forest-burning Mongol tribe,
-
the city is ravished
-
and brittle buildings of a hundred years
-
splash into the street;
-
and my eyes are burnt
-
for the embroidered Chinese girls,
-
already old,
-
and so small between the thin pines
-
on these enormous landscapes,
-
that if you turn your head
-
they are lost for hours.
-
-- Leonard Cohen
-
}
-
output.puts text
-
text
-
end
-
-
1
command "pessoa-poem", "" do
-
output.puts <<-TEXT
-
--
-
I've gone to bed with every feeling,
-
I've been the pimp of every emotion,
-
All felt sensations have bought me drinks,
-
I've traded glances with every motive for every act,
-
I've held hands with every urge to depart,
-
..
-
Rage, foam, the vastness that doesn't fit in my handkerchief,
-
The dog in heat howling in the night,
-
The pond from the farm going in circles around my insomnia,
-
The woods as they were, on our late-afternoon walks, the rose,
-
The indifferent tuft of hair, the moss, the pines,
-
The rage of not containing all this, not retaining all this,
-
O abstract hunger for things, impotent libido for moments,
-
Intellectual orgy of feeling life!
-
-- Fernando Pessoa
-
TEXT
-
end
-
-
1
command "test-ansi", "" do
-
prev_color = _pry_.config.color
-
_pry_.config.color = true
-
-
picture = unindent <<-'EOS'.gsub(/[[:alpha:]!]/) { |s| text.red(s) }
-
____ _______________________
-
/ \ | A W G |
-
/ O O \ | N I O N ! |
-
| | | S S R I ! |
-
\ \__/ / __| I K ! |
-
\____/ \________________________|
-
EOS
-
-
if windows_ansi?
-
move_up = proc { |n| "\e[#{n}F" }
-
else
-
move_up = proc { |n| "\e[#{n}A\e[0G" }
-
end
-
-
output.puts "\n" * 6
-
output.puts picture.lines.map(&:chomp).reverse.join(move_up[1])
-
output.puts "\n" * 6
-
output.puts "** ENV['TERM'] is #{ENV['TERM']} **\n\n"
-
-
_pry_.config.color = prev_color
-
end
-
end
-
end
-
1
class Pry
-
1
class Command::Edit < Pry::ClassCommand
-
1
require 'pry/commands/edit/exception_patcher'
-
1
require 'pry/commands/edit/file_and_line_locator'
-
-
1
match 'edit'
-
1
group 'Editing'
-
1
description 'Invoke the default editor on a file.'
-
-
1
banner <<-'BANNER'
-
Usage: edit [--no-reload|--reload|--patch] [--line LINE] [--temp|--ex|FILE[:LINE]|OBJECT|--in N]
-
-
Open a text editor. When no FILE is given, edits the pry input buffer.
-
When a method/module/command is given, the code is opened in an editor.
-
Ensure `Pry.config.editor` or `_pry_.config.editor` is set to your editor of choice.
-
-
edit sample.rb edit -p MyClass#my_method
-
edit sample.rb --line 105 edit MyClass
-
edit MyClass#my_method edit --ex
-
edit --method edit --ex -p
-
-
https://github.com/pry/pry/wiki/Editor-integration#wiki-Edit_command
-
BANNER
-
-
1
def options(opt)
-
opt.on :e, :ex, "Open the file that raised the most recent exception (_ex_.file)",
-
:optional_argument => true, :as => Integer
-
opt.on :i, :in, "Open a temporary file containing the Nth input expression. N may be a range",
-
:optional_argument => true, :as => Range, :default => -1..-1
-
opt.on :t, :temp, "Open an empty temporary file"
-
opt.on :l, :line, "Jump to this line in the opened file",
-
:argument => true, :as => Integer
-
opt.on :n, :"no-reload", "Don't automatically reload the edited file"
-
opt.on :c, :current, "Open the current __FILE__ and at __LINE__ (as returned by `whereami`)"
-
opt.on :r, :reload, "Reload the edited code immediately (default for ruby files)"
-
opt.on :p, :patch, "Instead of editing the object's file, try to edit in a tempfile and apply as a monkey patch"
-
opt.on :m, :method, "Explicitly edit the _current_ method (when inside a method context)."
-
end
-
-
1
def process
-
if bad_option_combination?
-
raise CommandError, "Only one of --ex, --temp, --in, --method and FILE may be specified."
-
end
-
-
if repl_edit?
-
# code defined in pry, eval'd within pry.
-
repl_edit
-
elsif runtime_patch?
-
# patch code without persisting changes
-
apply_runtime_patch
-
else
-
# code stored in actual files, eval'd at top-level
-
file_edit
-
end
-
end
-
-
1
def repl_edit?
-
!opts.present?(:ex) && !opts.present?(:current) && !opts.present?(:method) &&
-
filename_argument.empty?
-
end
-
-
1
def repl_edit
-
content = Pry::Editor.new(_pry_).edit_tempfile_with_content(initial_temp_file_content,
-
initial_temp_file_content.lines.count)
-
silence_warnings do
-
eval_string.replace content
-
end
-
end
-
-
1
def file_based_exception?
-
opts.present?(:ex) && !opts.present?(:patch)
-
end
-
-
1
def runtime_patch?
-
!file_based_exception? && (opts.present?(:patch) || pry_method?(code_object))
-
end
-
-
1
def apply_runtime_patch
-
if patch_exception?
-
ExceptionPatcher.new(_pry_, state, file_and_line_for_current_exception).perform_patch
-
else
-
if code_object.is_a?(Pry::Method)
-
code_object.redefine Pry::Editor.new(_pry_).edit_tempfile_with_content(code_object.source)
-
else
-
raise NotImplementedError, "Cannot yet patch #{code_object} objects!"
-
end
-
end
-
end
-
-
1
def ensure_file_name_is_valid(file_name)
-
raise CommandError, "Cannot find a valid file for #{filename_argument}" if !file_name
-
raise CommandError, "#{file_name} is not a valid file name, cannot edit!" if not_a_real_file?(file_name)
-
end
-
-
1
def file_and_line_for_current_exception
-
FileAndLineLocator.from_exception(_pry_.last_exception, opts[:ex].to_i)
-
end
-
-
1
def file_and_line
-
file_name, line = if opts.present?(:current)
-
FileAndLineLocator.from_binding(target)
-
elsif opts.present?(:ex)
-
file_and_line_for_current_exception
-
elsif code_object
-
FileAndLineLocator.from_code_object(code_object, filename_argument)
-
else
-
# when file and line are passed as a single arg, e.g my_file.rb:30
-
FileAndLineLocator.from_filename_argument(filename_argument)
-
end
-
-
[file_name, opts.present?(:line) ? opts[:l].to_i : line]
-
end
-
-
1
def file_edit
-
file_name, line = file_and_line
-
-
ensure_file_name_is_valid(file_name)
-
-
Pry::Editor.new(_pry_).invoke_editor(file_name, line, reload?(file_name))
-
set_file_and_dir_locals(file_name)
-
-
if reload?(file_name)
-
silence_warnings do
-
load file_name
-
end
-
end
-
end
-
-
1
def filename_argument
-
args.join(' ')
-
end
-
-
1
def code_object
-
@code_object ||= !probably_a_file?(filename_argument) &&
-
Pry::CodeObject.lookup(filename_argument, _pry_)
-
end
-
-
1
def pry_method?(code_object)
-
code_object.is_a?(Pry::Method) &&
-
code_object.pry_method?
-
end
-
-
1
def patch_exception?
-
opts.present?(:ex) && opts.present?(:patch)
-
end
-
-
1
def bad_option_combination?
-
[opts.present?(:ex), opts.present?(:temp),
-
opts.present?(:in), opts.present?(:method), !filename_argument.empty?].count(true) > 1
-
end
-
-
1
def input_expression
-
case opts[:i]
-
when Range
-
(_pry_.input_array[opts[:i]] || []).join
-
when Fixnum
-
_pry_.input_array[opts[:i]] || ""
-
else
-
raise Pry::CommandError, "Not a valid range: #{opts[:i]}"
-
end
-
end
-
-
1
def reloadable?
-
opts.present?(:reload) || opts.present?(:ex)
-
end
-
-
1
def never_reload?
-
opts.present?(:'no-reload') || _pry_.config.disable_auto_reload
-
end
-
-
1
def reload?(file_name="")
-
(reloadable? || file_name.end_with?(".rb")) && !never_reload?
-
end
-
-
1
def initial_temp_file_content
-
case
-
when opts.present?(:temp)
-
""
-
when opts.present?(:in)
-
input_expression
-
when eval_string.strip != ""
-
eval_string
-
else
-
_pry_.input_array.reverse_each.find { |x| x && x.strip != "" } || ""
-
end
-
end
-
-
1
def probably_a_file?(str)
-
[".rb", ".c", ".py", ".yml", ".gemspec"].include?(File.extname(str)) ||
-
str =~ /\/|\\/
-
end
-
end
-
-
1
Pry::Commands.add_command(Pry::Command::Edit)
-
end
-
1
class Pry
-
1
class Command::Edit
-
1
class ExceptionPatcher
-
1
attr_accessor :_pry_
-
1
attr_accessor :state
-
1
attr_accessor :file_and_line
-
-
1
def initialize(_pry_, state, exception_file_and_line)
-
@_pry_ = _pry_
-
@state = state
-
@file_and_line = exception_file_and_line
-
end
-
-
# perform the patch
-
1
def perform_patch
-
file_name, _ = file_and_line
-
lines = state.dynamical_ex_file || File.read(file_name)
-
-
source = Pry::Editor.new(_pry_).edit_tempfile_with_content(lines)
-
_pry_.evaluate_ruby source
-
state.dynamical_ex_file = source.split("\n")
-
end
-
end
-
end
-
end
-
1
class Pry
-
1
class Command::Edit
-
1
module FileAndLineLocator
-
1
class << self
-
1
def from_binding(target)
-
[target.eval("__FILE__"), target.eval("__LINE__")]
-
end
-
-
1
def from_code_object(code_object, filename_argument)
-
if File.exists?(code_object.source_file.to_s)
-
[code_object.source_file, code_object.source_line]
-
else
-
raise CommandError, "Cannot find a file for #{filename_argument}!"
-
end
-
end
-
-
1
def from_exception(exception, backtrace_level)
-
raise CommandError, "No exception found." if exception.nil?
-
-
file_name, line = exception.bt_source_location_for(backtrace_level)
-
raise CommandError, "Exception has no associated file." if file_name.nil?
-
raise CommandError, "Cannot edit exceptions raised in REPL." if Pry.eval_path == file_name
-
-
[file_name, line]
-
end
-
-
# when file and line are passed as a single arg, e.g my_file.rb:30
-
1
def from_filename_argument(filename_argument)
-
f = File.expand_path(filename_argument)
-
l = f.sub!(/:(\d+)$/, "") ? $1.to_i : 1
-
[f, l]
-
end
-
end
-
end
-
end
-
end
-
1
class Pry
-
1
class Command::Exit < Pry::ClassCommand
-
1
match 'exit'
-
1
group 'Navigating Pry'
-
1
description 'Pop the previous binding.'
-
1
command_options :keep_retval => true
-
-
1
banner <<-'BANNER'
-
Usage: exit [OPTIONS] [--help]
-
Aliases: quit
-
-
Pop the previous binding (does NOT exit program). It can be useful to exit a
-
context with a user-provided value. For instance an exit value can be used to
-
determine program flow.
-
-
exit "pry this"
-
exit
-
-
https://github.com/pry/pry/wiki/State-navigation#wiki-Exit_with_value
-
BANNER
-
-
1
def process
-
if _pry_.binding_stack.one?
-
_pry_.run_command "exit-all #{arg_string}"
-
else
-
# otherwise just pop a binding and return user supplied value
-
process_pop_and_return
-
end
-
end
-
-
1
def process_pop_and_return
-
popped_object = _pry_.binding_stack.pop.eval('self')
-
-
# return a user-specified value if given otherwise return the object
-
return target.eval(arg_string) unless arg_string.empty?
-
popped_object
-
end
-
end
-
-
1
Pry::Commands.add_command(Pry::Command::Exit)
-
1
Pry::Commands.alias_command 'quit', 'exit'
-
end
-
1
class Pry
-
1
class Command::ExitAll < Pry::ClassCommand
-
1
match 'exit-all'
-
1
group 'Navigating Pry'
-
1
description 'End the current Pry session.'
-
-
1
banner <<-'BANNER'
-
Usage: exit-all [--help]
-
Aliases: !!@
-
-
End the current Pry session (popping all bindings and returning to caller).
-
Accepts optional return value.
-
BANNER
-
-
1
def process
-
# calculate user-given value
-
exit_value = target.eval(arg_string)
-
-
# clear the binding stack
-
_pry_.binding_stack.clear
-
-
# break out of the repl loop
-
throw(:breakout, exit_value)
-
end
-
end
-
-
1
Pry::Commands.add_command(Pry::Command::ExitAll)
-
1
Pry::Commands.alias_command '!!@', 'exit-all'
-
end
-
1
class Pry
-
1
class Command::ExitProgram < Pry::ClassCommand
-
1
match 'exit-program'
-
1
group 'Navigating Pry'
-
1
description 'End the current program.'
-
-
1
banner <<-'BANNER'
-
Usage: exit-program [--help]
-
Aliases: quit-program
-
!!!
-
-
End the current program.
-
BANNER
-
-
1
def process
-
Kernel.exit target.eval(arg_string).to_i
-
end
-
end
-
-
1
Pry::Commands.add_command(Pry::Command::ExitProgram)
-
1
Pry::Commands.alias_command 'quit-program', 'exit-program'
-
1
Pry::Commands.alias_command '!!!', 'exit-program'
-
end
-
1
class Pry
-
1
class Command::FindMethod < Pry::ClassCommand
-
1
extend Pry::Helpers::BaseHelpers
-
-
1
match 'find-method'
-
1
group 'Context'
-
1
description 'Recursively search for a method within a Class/Module or the current namespace.'
-
1
command_options :shellwords => false
-
-
1
banner <<-'BANNER'
-
Usage: find-method [-n|-c] METHOD [NAMESPACE]
-
-
Recursively search for a method within a Class/Module or the current namespace.
-
Use the `-n` switch (the default) to search for methods whose name matches the
-
given regex. Use the `-c` switch to search for methods that contain the given
-
code.
-
-
# Find all methods whose name match /re/ inside
-
# the Pry namespace. Matches Pry#repl, etc.
-
find-method re Pry
-
-
# Find all methods that contain the code:
-
# output.puts inside the Pry namepsace.
-
find-method -c 'output.puts' Pry
-
BANNER
-
-
1
def options(opt)
-
opt.on :n, :name, "Search for a method by name"
-
opt.on :c, :content, "Search for a method based on content in Regex form"
-
end
-
-
1
def process
-
return if args.size < 1
-
klass = search_class
-
-
matches = if opts.content?
-
content_search(klass)
-
else
-
name_search(klass)
-
end
-
-
show_search_results(matches)
-
end
-
-
1
private
-
-
# @return [Regexp] The pattern to search for.
-
1
def pattern
-
@pattern ||= ::Regexp.new args[0]
-
end
-
-
# Output the result of the search.
-
#
-
# @param [Array] matches
-
1
def show_search_results(matches)
-
if matches.empty?
-
output.puts text.bold("No Methods Matched")
-
else
-
print_matches(matches)
-
end
-
end
-
-
# The class to search for methods.
-
# We only search classes, so if the search object is an
-
# instance, return its class. If no search object is given
-
# search `target_self`.
-
1
def search_class
-
klass = if args[1]
-
target.eval(args[1])
-
else
-
target_self
-
end
-
-
klass.is_a?(Module) ? klass : klass.class
-
end
-
-
# pretty-print a list of matching methods.
-
#
-
# @param [Array<Method>] matches
-
1
def print_matches(matches)
-
grouped = matches.group_by(&:owner)
-
order = grouped.keys.sort_by{ |x| x.name || x.to_s }
-
-
order.each do |klass|
-
print_matches_for_class(klass, grouped)
-
end
-
end
-
-
# Print matched methods for a class
-
1
def print_matches_for_class(klass, grouped)
-
output.puts text.bold(klass.name)
-
grouped[klass].each do |method|
-
header = method.name_with_owner
-
output.puts header + additional_info(header, method)
-
end
-
end
-
-
# Return the matched lines of method source if `-c` is given or ""
-
# if `-c` was not given
-
1
def additional_info(header, method)
-
if opts.content?
-
": " << colorize_code(matched_method_lines(header, method))
-
else
-
""
-
end
-
end
-
-
1
def matched_method_lines(header, method)
-
method.source.split(/\n/).select {|x| x =~ pattern }.join("\n#{' ' * header.length}")
-
end
-
-
# Run the given block against every constant in the provided namespace.
-
#
-
# @param [Module] klass The namespace in which to start the search.
-
# @param [Hash<Module,Boolean>] done The namespaces we've already visited (private)
-
# @yieldparam klass Each class/module in the namespace.
-
#
-
1
def recurse_namespace(klass, done={}, &block)
-
return if !(Module === klass) || done[klass]
-
-
done[klass] = true
-
-
yield klass
-
-
klass.constants.each do |name|
-
next if klass.autoload?(name)
-
begin
-
const = klass.const_get(name)
-
rescue RescuableException
-
# constant loading is an inexact science at the best of times,
-
# this often happens when a constant was .autoload? but someone
-
# tried to load it. It's now not .autoload? but will still raise
-
# a NameError when you access it.
-
else
-
recurse_namespace(const, done, &block)
-
end
-
end
-
end
-
-
# Gather all the methods in a namespace that pass the given block.
-
#
-
# @param [Module] namespace The namespace in which to search.
-
# @yieldparam [Method] method The method to test
-
# @yieldreturn [Boolean]
-
# @return [Array<Method>]
-
#
-
1
def search_all_methods(namespace)
-
done = Hash.new{ |h,k| h[k] = {} }
-
matches = []
-
-
recurse_namespace(namespace) do |klass|
-
(Pry::Method.all_from_class(klass) + Pry::Method.all_from_obj(klass)).each do |method|
-
next if done[method.owner][method.name]
-
done[method.owner][method.name] = true
-
-
matches << method if yield method
-
end
-
end
-
-
matches
-
end
-
-
# Search for all methods with a name that matches the given regex
-
# within a namespace.
-
#
-
# @param [Module] namespace The namespace to search
-
# @return [Array<Method>]
-
#
-
1
def name_search(namespace)
-
search_all_methods(namespace) do |meth|
-
meth.name =~ pattern
-
end
-
end
-
-
# Search for all methods who's implementation matches the given regex
-
# within a namespace.
-
#
-
# @param [Module] namespace The namespace to search
-
# @return [Array<Method>]
-
#
-
1
def content_search(namespace)
-
search_all_methods(namespace) do |meth|
-
begin
-
meth.source =~ pattern
-
rescue RescuableException
-
false
-
end
-
end
-
end
-
end
-
-
1
Pry::Commands.add_command(Pry::Command::FindMethod)
-
end
-
1
class Pry
-
1
class Command::FixIndent < Pry::ClassCommand
-
1
match 'fix-indent'
-
1
group 'Input and Output'
-
-
1
description "Correct the indentation for contents of the input buffer"
-
-
1
banner <<-USAGE
-
Usage: fix-indent
-
USAGE
-
-
1
def process
-
indented_str = Pry::Indent.indent(eval_string)
-
eval_string.replace indented_str
-
end
-
end
-
-
1
Pry::Commands.add_command(Pry::Command::FixIndent)
-
end
-
1
class Pry
-
1
class Command::GemCd < Pry::ClassCommand
-
1
match 'gem-cd'
-
1
group 'Gems'
-
1
description "Change working directory to specified gem's directory."
-
1
command_options :argument_required => true
-
-
1
banner <<-'BANNER'
-
Usage: gem-cd GEM_NAME
-
-
Change the current working directory to that in which the given gem is
-
installed.
-
BANNER
-
-
1
def process(gem)
-
Dir.chdir(Rubygem.spec(gem).full_gem_path)
-
output.puts(Dir.pwd)
-
end
-
-
1
def complete(str)
-
Rubygem.complete(str)
-
end
-
end
-
-
1
Pry::Commands.add_command(Pry::Command::GemCd)
-
end
-
1
class Pry
-
1
class Command::GemInstall < Pry::ClassCommand
-
1
match 'gem-install'
-
1
group 'Gems'
-
1
description 'Install a gem and refresh the gem cache.'
-
1
command_options :argument_required => true
-
-
1
banner <<-'BANNER'
-
Usage: gem-install GEM_NAME
-
-
Installs the given gem, refreshes the gem cache, and requires the gem for you
-
based on a best guess from the gem name.
-
-
gem-install pry-stack_explorer
-
BANNER
-
-
1
def setup
-
require 'rubygems/dependency_installer' unless defined? Gem::DependencyInstaller
-
end
-
-
1
def process(gem)
-
Rubygem.install(gem)
-
output.puts "Gem `#{ text.green(gem) }` installed."
-
require gem
-
rescue LoadError
-
require_path = gem.split('-').join('/')
-
require require_path
-
end
-
end
-
-
1
Pry::Commands.add_command(Pry::Command::GemInstall)
-
end
-
1
class Pry
-
1
class Command::GemList < Pry::ClassCommand
-
1
match 'gem-list'
-
1
group 'Gems'
-
1
description 'List and search installed gems.'
-
-
1
banner <<-'BANNER'
-
Usage: gem-list [REGEX]
-
-
List all installed gems, when a regex is provided, limit the output to those
-
that match the regex.
-
BANNER
-
-
1
def process(pattern = nil)
-
pattern = Regexp.compile(pattern || '')
-
gems = Rubygem.list(pattern).group_by(&:name)
-
-
gems.each do |gem, specs|
-
specs.sort! do |a,b|
-
Gem::Version.new(b.version) <=> Gem::Version.new(a.version)
-
end
-
-
versions = specs.each_with_index.map do |spec, index|
-
index == 0 ? text.bright_green(spec.version.to_s) : text.green(spec.version.to_s)
-
end
-
-
output.puts "#{text.default gem} (#{versions.join ', '})"
-
end
-
end
-
end
-
-
1
Pry::Commands.add_command(Pry::Command::GemList)
-
end
-
1
class Pry
-
1
class Command::GemOpen < Pry::ClassCommand
-
1
match 'gem-open'
-
1
group 'Gems'
-
1
description 'Opens the working directory of the gem in your editor.'
-
1
command_options :argument_required => true
-
-
1
banner <<-'BANNER'
-
Usage: gem-open GEM_NAME
-
-
Change the current working directory to that in which the given gem is
-
installed, and then opens your text editor.
-
-
gem-open pry-exception_explorer
-
BANNER
-
-
1
def process(gem)
-
Dir.chdir(Rubygem.spec(gem).full_gem_path) do
-
Pry::Editor.invoke_editor(".", 0, false)
-
end
-
end
-
-
1
def complete(str)
-
Rubygem.complete(str)
-
end
-
end
-
-
1
Pry::Commands.add_command(Pry::Command::GemOpen)
-
end
-
1
class Pry
-
1
class Command::Gist < Pry::ClassCommand
-
1
match 'gist'
-
1
group 'Misc'
-
1
description 'Upload code, docs, history to https://gist.github.com/.'
-
1
command_options :requires_gem => "gist"
-
-
1
banner <<-'BANNER'
-
Usage: gist [OPTIONS] [--help]
-
-
The gist command enables you to gist code from files and methods to github.
-
-
gist -i 20 --lines 1..3
-
gist Pry#repl --lines 1..-1
-
gist Rakefile --lines 5
-
BANNER
-
-
1
def setup
-
require 'gist'
-
end
-
-
1
def options(opt)
-
CodeCollector.inject_options(opt)
-
opt.on :login, "Authenticate the gist gem with GitHub"
-
opt.on :p, :public, "Create a public gist (default: false)", :default => false
-
opt.on :clip, "Copy the selected content to clipboard instead, do NOT gist it", :default => false
-
end
-
-
1
def process
-
return ::Gist.login! if opts.present?(:login)
-
cc = CodeCollector.new(args, opts, _pry_)
-
-
if cc.content =~ /\A\s*\z/
-
raise CommandError, "Found no code to gist."
-
end
-
-
if opts.present?(:clip)
-
clipboard_content(cc.content)
-
else
-
# we're overriding the default behavior of the 'in' option (as
-
# defined on CodeCollector) with our local behaviour.
-
content = opts.present?(:in) ? input_content : cc.content
-
gist_content content, cc.file
-
end
-
end
-
-
1
def clipboard_content(content)
-
::Gist.copy(content)
-
output.puts "Copied content to clipboard!"
-
end
-
-
1
def input_content
-
content = ""
-
CodeCollector.input_expression_ranges.each do |range|
-
input_expressions = _pry_.input_array[range] || []
-
Array(input_expressions).each_with_index do |code, index|
-
corrected_index = index + range.first
-
if code && code != ""
-
content << code
-
if code !~ /;\Z/
-
content << "#{comment_expression_result_for_gist(_pry_.config.gist.inspecter.call(_pry_.output_array[corrected_index]))}"
-
end
-
end
-
end
-
end
-
-
content
-
end
-
-
1
def comment_expression_result_for_gist(result)
-
content = ""
-
result.lines.each_with_index do |line, index|
-
if index == 0
-
content << "# => #{line}"
-
else
-
content << "# #{line}"
-
end
-
end
-
-
content
-
end
-
-
1
def gist_content(content, filename)
-
response = ::Gist.gist(content, :filename => filename || "pry_gist.rb", :public => !!opts[:p])
-
if response
-
url = response['html_url']
-
message = "Gist created at URL #{url}"
-
begin
-
::Gist.copy(url)
-
message << ", which is now in the clipboard."
-
rescue ::Gist::ClipboardError
-
end
-
-
output.puts message
-
end
-
end
-
end
-
-
1
Pry::Commands.add_command(Pry::Command::Gist)
-
1
Pry::Commands.alias_command 'clipit', 'gist --clip'
-
end
-
1
class Pry
-
1
class Command::Help < Pry::ClassCommand
-
1
match 'help'
-
1
group 'Help'
-
1
description 'Show a list of commands or information about a specific command.'
-
-
1
banner <<-'BANNER'
-
Usage: help [COMMAND]
-
-
With no arguments, help lists all the available commands along with their
-
descriptions. When given a command name as an argument, shows the help
-
for that command.
-
BANNER
-
-
# We only want to show commands that have descriptions, so that the
-
# easter eggs don't show up.
-
1
def visible_commands
-
visible = {}
-
commands.each do |key, command|
-
visible[key] = command if command.description && !command.description.empty?
-
end
-
visible
-
end
-
-
# Get a hash of available commands grouped by the "group" name.
-
1
def command_groups
-
visible_commands.values.group_by(&:group)
-
end
-
-
1
def process
-
if args.empty?
-
display_index(command_groups)
-
else
-
display_search(args.first)
-
end
-
end
-
-
# Display the index view, with headings and short descriptions per command.
-
#
-
# @param [Hash<String, Array<Commands>>] groups
-
1
def display_index(groups)
-
help_text = []
-
-
sorted_group_names(groups).each do |group_name|
-
commands = sorted_commands(groups[group_name])
-
-
if commands.any?
-
help_text << help_text_for_commands(group_name, commands)
-
end
-
end
-
-
_pry_.pager.page help_text.join("\n\n")
-
end
-
-
# Given a group name and an array of commands,
-
# return the help string for those commands.
-
#
-
# @param [String] name The group name.
-
# @param [Array<Pry::Command>] commands
-
# @return [String] The generated help string.
-
1
def help_text_for_commands(name, commands)
-
"#{text.bold(name.capitalize)}\n" << commands.map do |command|
-
" #{command.options[:listing].to_s.ljust(18)} #{command.description.capitalize}"
-
end.join("\n")
-
end
-
-
# @param [Hash] groups
-
# @return [Array<String>] An array of sorted group names.
-
1
def sorted_group_names(groups)
-
groups.keys.sort_by(&method(:group_sort_key))
-
end
-
-
# Sort an array of commands by their `listing` name.
-
#
-
# @param [Array<Pry::Command>] commands The commands to sort
-
# @return [Array<Pry::Command>] commands sorted by listing name.
-
1
def sorted_commands(commands)
-
commands.sort_by{ |command| command.options[:listing].to_s }
-
end
-
-
# Display help for an individual command or group.
-
#
-
# @param [String] search The string to search for.
-
1
def display_search(search)
-
if command = command_set.find_command_for_help(search)
-
display_command(command)
-
else
-
display_filtered_search_results(search)
-
end
-
end
-
-
# Display help for a searched item, filtered first by group
-
# and if that fails, filtered by command name.
-
#
-
# @param [String] search The string to search for.
-
1
def display_filtered_search_results(search)
-
groups = search_hash(search, command_groups)
-
-
if groups.size > 0
-
display_index(groups)
-
else
-
display_filtered_commands(search)
-
end
-
end
-
-
# Display help for a searched item, filtered by group
-
#
-
# @param [String] search The string to search for.
-
1
def display_filtered_commands(search)
-
filtered = search_hash(search, visible_commands)
-
raise CommandError, "No help found for '#{args.first}'" if filtered.empty?
-
-
if filtered.size == 1
-
display_command(filtered.values.first)
-
else
-
display_index({"'#{search}' commands" => filtered.values})
-
end
-
end
-
-
# Display help for an individual command.
-
#
-
# @param [Pry::Command] command
-
1
def display_command(command)
-
_pry_.pager.page command.new.help
-
end
-
-
# Find a subset of a hash that matches the user's search term.
-
#
-
# If there's an exact match a Hash of one element will be returned,
-
# otherwise a sub-Hash with every key that matches the search will
-
# be returned.
-
#
-
# @param [String] search the search term
-
# @param [Hash] hash the hash to search
-
1
def search_hash(search, hash)
-
matching = {}
-
-
hash.each_pair do |key, value|
-
next unless key.is_a?(String)
-
if normalize(key) == normalize(search)
-
return {key => value}
-
elsif normalize(key).start_with?(normalize(search))
-
matching[key] = value
-
end
-
end
-
-
matching
-
end
-
-
# Clean search terms to make it easier to search group names
-
#
-
# @param [String] key
-
# @return [String]
-
1
def normalize(key)
-
key.downcase.gsub(/pry\W+/, '')
-
end
-
-
1
def group_sort_key(group_name)
-
[%w(Help Context Editing Introspection Input_and_output Navigating_pry Gems Basic Commands).index(group_name.gsub(' ', '_')) || 99, group_name]
-
end
-
end
-
-
1
Pry::Commands.add_command(Pry::Command::Help)
-
end
-
1
class Pry
-
1
class Command::Hist < Pry::ClassCommand
-
1
match 'hist'
-
1
group 'Editing'
-
1
description 'Show and replay Readline history.'
-
-
1
banner <<-'BANNER'
-
Usage: hist [--head|--tail]
-
hist --all
-
hist --head N
-
hist --tail N
-
hist --show START..END
-
hist --grep PATTERN
-
hist --clear
-
hist --replay START..END
-
hist --save [START..END] FILE
-
Aliases: history
-
-
Show and replay Readline history.
-
BANNER
-
-
1
def options(opt)
-
opt.on :a, :all, "Display all history"
-
opt.on :H, :head, "Display the first N items", :optional_argument => true, :as => Integer
-
opt.on :T, :tail, "Display the last N items", :optional_argument => true, :as => Integer
-
opt.on :s, :show, "Show the given range of lines", :optional_argument => true, :as => Range
-
opt.on :G, :grep, "Show lines matching the given pattern", :argument => true, :as => String
-
opt.on :c, :clear , "Clear the current session's history"
-
opt.on :r, :replay, "Replay a line or range of lines", :argument => true, :as => Range
-
opt.on :save, "Save history to a file", :argument => true, :as => Range
-
opt.on :e, :'exclude-pry', "Exclude Pry commands from the history"
-
opt.on :n, :'no-numbers', "Omit line numbers"
-
end
-
-
1
def process
-
@history = find_history
-
-
if opts.present?(:show)
-
@history = @history.between(opts[:show])
-
end
-
-
if opts.present?(:grep)
-
@history = @history.grep(opts[:grep])
-
end
-
-
@history = case
-
when opts.present?(:head)
-
@history.take_lines(1, opts[:head] || 10)
-
when opts.present?(:tail)
-
@history.take_lines(-(opts[:tail] || 10), opts[:tail] || 10)
-
when opts.present?(:show)
-
@history.between(opts[:show])
-
else
-
@history
-
end
-
-
if opts.present?(:'exclude-pry')
-
@history = @history.select do |loc|
-
!command_set.valid_command?(loc.line)
-
end
-
end
-
-
if opts.present?(:save)
-
process_save
-
elsif opts.present?(:clear)
-
process_clear
-
elsif opts.present?(:replay)
-
process_replay
-
else
-
process_display
-
end
-
end
-
-
1
private
-
-
1
def process_display
-
unless opts.present?(:'no-numbers')
-
@history = @history.with_line_numbers
-
end
-
-
_pry_.pager.open do |pager|
-
@history.print_to_output(pager, true)
-
end
-
end
-
-
1
def process_save
-
case opts[:save]
-
when Range
-
@history = @history.between(opts[:save])
-
-
unless args.first
-
raise CommandError, "Must provide a file name."
-
end
-
-
file_name = File.expand_path(args.first)
-
when String
-
file_name = File.expand_path(opts[:save])
-
end
-
-
output.puts "Saving history in #{file_name}..."
-
-
File.open(file_name, 'w') { |f| f.write(@history.raw) }
-
-
output.puts "History saved."
-
end
-
-
1
def process_clear
-
Pry.history.clear
-
output.puts "History cleared."
-
end
-
-
1
def process_replay
-
@history = @history.between(opts[:r])
-
replay_sequence = @history.raw
-
-
# If we met follow-up "hist" call, check for the "--replay" option
-
# presence. If "hist" command is called with other options, proceed
-
# further.
-
check_for_juxtaposed_replay(replay_sequence)
-
-
replay_sequence.lines.each do |line|
-
_pry_.eval line, :generated => true
-
end
-
end
-
-
# Checks +replay_sequence+ for the presence of neighboring replay calls.
-
# @example
-
# [1] pry(main)> hist --show 46894
-
# 46894: hist --replay 46675..46677
-
# [2] pry(main)> hist --show 46675..46677
-
# 46675: 1+1
-
# 46676: a = 100
-
# 46677: hist --tail
-
# [3] pry(main)> hist --replay 46894
-
# Error: Replay index 46894 points out to another replay call: `hist -r 46675..46677`
-
# [4] pry(main)>
-
#
-
# @raise [Pry::CommandError] If +replay_sequence+ contains another
-
# "hist --replay" call
-
# @param [String] replay_sequence The sequence of commands to be replayed
-
# (per saltum)
-
# @return [Boolean] `false` if +replay_sequence+ does not contain another
-
# "hist --replay" call
-
1
def check_for_juxtaposed_replay(replay_sequence)
-
if replay_sequence =~ /\Ahist(?:ory)?\b/
-
# Create *fresh* instance of Options for parsing of "hist" command.
-
_slop = self.slop
-
_slop.parse replay_sequence.split(' ')[1..-1]
-
-
if _slop.present?(:r)
-
replay_sequence = replay_sequence.split("\n").join('; ')
-
index = opts[:r]
-
index = index.min if index.min == index.max || index.max.nil?
-
-
raise CommandError, "Replay index #{ index } points out to another replay call: `#{ replay_sequence }`"
-
end
-
else
-
false
-
end
-
end
-
-
# Finds history depending on the given switch.
-
#
-
# @return [Pry::Code] if it finds `--all` (or `-a`) switch, returns all
-
# entries in history. Without the switch returns only the entries from the
-
# current Pry session.
-
1
def find_history
-
h = if opts.present?(:all)
-
Pry.history.to_a
-
else
-
Pry.history.to_a.last(Pry.history.session_line_count)
-
end
-
# The last value in history will be the 'hist' command itself.
-
Pry::Code(h[0..-2])
-
end
-
end
-
-
1
Pry::Commands.add_command(Pry::Command::Hist)
-
1
Pry::Commands.alias_command 'history', 'hist'
-
end
-
1
class Pry
-
1
class Command::ImportSet < Pry::ClassCommand
-
1
match 'import-set'
-
1
group 'Commands'
-
# TODO: Provide a better description with examples and a general conception
-
# of this command.
-
1
description 'Import a Pry command set.'
-
-
1
banner <<-'BANNER'
-
Import a Pry command set.
-
BANNER
-
-
1
def process(command_set_name)
-
raise CommandError, "Provide a command set name" if command_set.nil?
-
-
set = target.eval(arg_string)
-
_pry_.commands.import set
-
end
-
end
-
-
1
Pry::Commands.add_command(Pry::Command::ImportSet)
-
end
-
1
class Pry
-
1
class Command::InstallCommand < Pry::ClassCommand
-
1
match 'install-command'
-
1
group 'Commands'
-
1
description 'Install a disabled command.'
-
-
1
banner <<-'BANNER'
-
Usage: install-command COMMAND
-
-
Installs the gems necessary to run the given COMMAND. You will generally not
-
need to run this unless told to by an error message.
-
BANNER
-
-
1
def process(name)
-
require 'rubygems/dependency_installer' unless defined? Gem::DependencyInstaller
-
command = find_command(name)
-
-
unless command
-
output.puts "Command #{ text.green(name) } is not found"
-
return
-
end
-
-
if command_dependencies_met?(command.options)
-
output.puts "Dependencies for #{ text.green(name) } are met. Nothing to do"
-
return
-
end
-
-
output.puts "Attempting to install #{ text.green(name) } command..."
-
gems_to_install = Array(command.options[:requires_gem])
-
-
gems_to_install.each do |g|
-
next if Rubygem.installed?(g)
-
output.puts "Installing #{ text.green(g) } gem..."
-
Rubygem.install(g)
-
end
-
-
gems_to_install.each do |g|
-
begin
-
require g
-
rescue LoadError
-
fail_msg = "Required gem #{ text.green(g) } installed but not found."
-
fail_msg += " Aborting command installation\n"
-
fail_msg += 'Tips: 1. Check your PATH; 2. Run `bundle update`'
-
raise CommandError, fail_msg
-
end
-
end
-
-
output.puts "Installation of #{ text.green(name) } successful! Type `help #{name}` for information"
-
end
-
end
-
-
1
Pry::Commands.add_command(Pry::Command::InstallCommand)
-
end
-
1
class Pry
-
1
class Command::JumpTo < Pry::ClassCommand
-
1
match 'jump-to'
-
1
group 'Navigating Pry'
-
1
description 'Jump to a binding further up the stack.'
-
-
1
banner <<-'BANNER'
-
Jump to a binding further up the stack, popping all bindings below.
-
BANNER
-
-
1
def process(break_level)
-
break_level = break_level.to_i
-
nesting_level = _pry_.binding_stack.size - 1
-
-
case break_level
-
when nesting_level
-
output.puts "Already at nesting level #{nesting_level}"
-
when (0...nesting_level)
-
_pry_.binding_stack.slice!(break_level + 1, _pry_.binding_stack.size)
-
-
else
-
max_nest_level = nesting_level - 1
-
output.puts "Invalid nest level. Must be between 0 and #{max_nest_level}. Got #{break_level}."
-
end
-
end
-
end
-
-
1
Pry::Commands.add_command(Pry::Command::JumpTo)
-
end
-
1
class Pry::Command::ListInspectors < Pry::ClassCommand
-
1
match 'list-inspectors'
-
1
group 'Input and Output'
-
1
description 'List the inspector procs available for use.'
-
1
banner <<-BANNER
-
Usage: list-inspectors
-
-
List the inspector procs available to print return values. You can use
-
change-inspector to switch between them.
-
BANNER
-
-
1
def process
-
output.puts heading("Available inspectors") + "\n"
-
inspector_map.each do |name, inspector|
-
output.write "Name: #{text.bold(name)}"
-
output.puts selected_inspector?(inspector) ? selected_text : ""
-
output.puts inspector[:description]
-
output.puts
-
end
-
end
-
-
1
private
-
1
def inspector_map
-
Pry::Inspector::MAP
-
end
-
-
1
def selected_text
-
text.red " (selected) "
-
end
-
-
1
def selected_inspector?(inspector)
-
_pry_.print == inspector[:value]
-
end
-
1
Pry::Commands.add_command(self)
-
end
-
1
class Pry::Command::ListPrompts < Pry::ClassCommand
-
1
match 'list-prompts'
-
1
group 'Input and Output'
-
1
description 'List the prompts available for use.'
-
1
banner <<-BANNER
-
Usage: list-prompts
-
-
List the available prompts. You can use change-prompt to switch between
-
them.
-
BANNER
-
-
1
def process
-
output.puts heading("Available prompts") + "\n"
-
prompt_map.each do |name, prompt|
-
output.write "Name: #{text.bold(name)}"
-
output.puts selected_prompt?(prompt) ? selected_text : ""
-
output.puts prompt[:description]
-
output.puts
-
end
-
end
-
-
1
private
-
1
def prompt_map
-
Pry::Prompt::MAP
-
end
-
-
1
def selected_text
-
text.red " (selected) "
-
end
-
-
1
def selected_prompt?(prompt)
-
_pry_.prompt == prompt[:value]
-
end
-
1
Pry::Commands.add_command(self)
-
end
-
1
require 'pry/commands/ls/ls_entity'
-
1
class Pry
-
1
class Command::Ls < Pry::ClassCommand
-
1
DEFAULT_OPTIONS = {
-
:heading_color => :bright_blue,
-
:public_method_color => :default,
-
:private_method_color => :blue,
-
:protected_method_color => :blue,
-
:method_missing_color => :bright_red,
-
:local_var_color => :yellow,
-
:pry_var_color => :default, # e.g. _, _pry_, _file_
-
:instance_var_color => :blue, # e.g. @foo
-
:class_var_color => :bright_blue, # e.g. @@foo
-
:global_var_color => :default, # e.g. $CODERAY_DEBUG, $eventmachine_library
-
:builtin_global_color => :cyan, # e.g. $stdin, $-w, $PID
-
:pseudo_global_color => :cyan, # e.g. $~, $1..$9, $LAST_MATCH_INFO
-
:constant_color => :default, # e.g. VERSION, ARGF
-
:class_constant_color => :blue, # e.g. Object, Kernel
-
:exception_constant_color => :magenta, # e.g. Exception, RuntimeError
-
:unloaded_constant_color => :yellow, # Any constant that is still in .autoload? state
-
:separator => " ",
-
:ceiling => [Object, Module, Class]
-
}
-
-
-
1
match 'ls'
-
1
group 'Context'
-
1
description 'Show the list of vars and methods in the current scope.'
-
1
command_options :shellwords => false, :interpolate => false
-
-
1
banner <<-'BANNER'
-
Usage: ls [-m|-M|-p|-pM] [-q|-v] [-c|-i] [Object]
-
ls [-g] [-l]
-
-
ls shows you which methods, constants and variables are accessible to Pry. By
-
default it shows you the local variables defined in the current shell, and any
-
public methods or instance variables defined on the current object.
-
-
The colours used are configurable using Pry.config.ls.*_color, and the separator
-
is Pry.config.ls.separator.
-
-
Pry.config.ls.ceiling is used to hide methods defined higher up in the
-
inheritance chain, this is by default set to [Object, Module, Class] so that
-
methods defined on all Objects are omitted. The -v flag can be used to ignore
-
this setting and show all methods, while the -q can be used to set the ceiling
-
much lower and show only methods defined on the object or its direct class.
-
-
Also check out `find-method` command (run `help find-method`).
-
BANNER
-
-
-
1
def options(opt)
-
opt.on :m, :methods, "Show public methods defined on the Object"
-
opt.on :M, "instance-methods", "Show public methods defined in a Module or Class"
-
opt.on :p, :ppp, "Show public, protected (in yellow) and private (in green) methods"
-
opt.on :q, :quiet, "Show only methods defined on object.singleton_class and object.class"
-
opt.on :v, :verbose, "Show methods and constants on all super-classes (ignores Pry.config.ls.ceiling)"
-
opt.on :g, :globals, "Show global variables, including those builtin to Ruby (in cyan)"
-
opt.on :l, :locals, "Show hash of local vars, sorted by descending size"
-
opt.on :c, :constants, "Show constants, highlighting classes (in blue), and exceptions (in purple).\n" <<
-
" " * 32 << "Constants that are pending autoload? are also shown (in yellow)"
-
opt.on :i, :ivars, "Show instance variables (in blue) and class variables (in bright blue)"
-
opt.on :G, :grep, "Filter output by regular expression", :argument => true
-
-
if jruby?
-
opt.on :J, "all-java", "Show all the aliases for methods from java (default is to show only prettiest)"
-
end
-
end
-
-
# Exclude -q, -v and --grep because they,
-
# don't specify what the user wants to see.
-
1
def no_user_opts?
-
!(opts[:methods] || opts['instance-methods'] || opts[:ppp] ||
-
opts[:globals] || opts[:locals] || opts[:constants] || opts[:ivars])
-
end
-
-
1
def process
-
@interrogatee = args.empty? ? target_self : target.eval(args.join(' '))
-
raise_errors_if_arguments_are_weird
-
ls_entity = LsEntity.new({
-
:interrogatee => @interrogatee,
-
:no_user_opts => no_user_opts?,
-
:opts => opts,
-
:args => args,
-
:_pry_ => _pry_
-
})
-
-
_pry_.pager.page ls_entity.entities_table
-
end
-
-
1
private
-
-
1
def error_list
-
any_args = args.any?
-
non_mod_interrogatee = !(Module === @interrogatee)
-
[
-
['-l does not make sense with a specified Object', :locals, any_args],
-
['-g does not make sense with a specified Object', :globals, any_args],
-
['-q does not make sense with -v', :quiet, opts.present?(:verbose)],
-
['-M only makes sense with a Module or a Class', 'instance-methods', non_mod_interrogatee],
-
['-c only makes sense with a Module or a Class', :constants, any_args && non_mod_interrogatee]
-
]
-
end
-
-
1
def raise_errors_if_arguments_are_weird
-
error_list.each do |message, option, invalid_expr|
-
raise Pry::CommandError, message if opts.present?(option) && invalid_expr
-
end
-
end
-
-
end
-
-
1
Pry::Commands.add_command(Pry::Command::Ls)
-
end
-
1
require 'pry/commands/ls/interrogatable'
-
-
1
class Pry
-
1
class Command::Ls < Pry::ClassCommand
-
1
class Constants < Pry::Command::Ls::Formatter
-
1
include Pry::Command::Ls::Interrogatable
-
-
-
1
def initialize(interrogatee, no_user_opts, opts, _pry_)
-
super(_pry_)
-
@interrogatee = interrogatee
-
@no_user_opts = no_user_opts
-
@default_switch = opts[:constants]
-
@verbose_switch = opts[:verbose]
-
end
-
-
1
def correct_opts?
-
super || (@no_user_opts && interrogating_a_module?)
-
end
-
-
1
def output_self
-
mod = interrogatee_mod
-
constants = WrappedModule.new(mod).constants(@verbose_switch)
-
output_section('constants', grep.regexp[format(mod, constants)])
-
end
-
-
1
private
-
-
1
def format(mod, constants)
-
constants.sort_by(&:downcase).map do |name|
-
if const = (!mod.autoload?(name) && (mod.const_get(name) || true) rescue nil)
-
if (const < Exception rescue false)
-
color(:exception_constant, name)
-
elsif (Module === mod.const_get(name) rescue false)
-
color(:class_constant, name)
-
else
-
color(:constant, name)
-
end
-
else
-
color(:unloaded_constant, name)
-
end
-
end
-
end
-
-
end
-
end
-
end
-
1
class Pry
-
1
class Command::Ls < Pry::ClassCommand
-
1
class Formatter
-
1
attr_writer :grep
-
1
attr_reader :_pry_
-
-
1
def initialize(_pry_)
-
@_pry_ = _pry_
-
@target = _pry_.current_context
-
end
-
-
1
def write_out
-
return false unless correct_opts?
-
output_self
-
end
-
-
1
private
-
-
1
def color(type, str)
-
Pry::Helpers::Text.send _pry_.config.ls["#{type}_color"], str
-
end
-
-
# Add a new section to the output.
-
# Outputs nothing if the section would be empty.
-
1
def output_section(heading, body)
-
return '' if body.compact.empty?
-
fancy_heading = Pry::Helpers::Text.bold(color(:heading, heading))
-
Pry::Helpers.tablify_or_one_line(fancy_heading, body)
-
end
-
-
1
def format_value(value)
-
Pry::ColorPrinter.pp(value, '')
-
end
-
-
1
def correct_opts?
-
@default_switch
-
end
-
-
1
def output_self
-
raise NotImplementedError
-
end
-
-
1
def grep
-
@grep || proc { |x| x }
-
end
-
-
end
-
end
-
end
-
1
class Pry
-
1
class Command::Ls < Pry::ClassCommand
-
1
class Globals < Pry::Command::Ls::Formatter
-
-
# Taken from "puts global_variables.inspect".
-
1
BUILTIN_GLOBALS =
-
%w($" $$ $* $, $-0 $-F $-I $-K $-W $-a $-d $-i $-l $-p $-v $-w $. $/ $\\
-
$: $; $< $= $> $0 $ARGV $CONSOLE $DEBUG $DEFAULT_INPUT $DEFAULT_OUTPUT
-
$FIELD_SEPARATOR $FILENAME $FS $IGNORECASE $INPUT_LINE_NUMBER
-
$INPUT_RECORD_SEPARATOR $KCODE $LOADED_FEATURES $LOAD_PATH $NR $OFS
-
$ORS $OUTPUT_FIELD_SEPARATOR $OUTPUT_RECORD_SEPARATOR $PID $PROCESS_ID
-
$PROGRAM_NAME $RS $VERBOSE $deferr $defout $stderr $stdin $stdout)
-
-
# `$SAFE` and `$?` are thread-local, the exception stuff only works in a
-
# rescue clause, everything else is basically a local variable with a `$`
-
# in its name.
-
1
PSEUDO_GLOBALS =
-
%w($! $' $& $` $@ $? $+ $_ $~ $1 $2 $3 $4 $5 $6 $7 $8 $9
-
$CHILD_STATUS $SAFE $ERROR_INFO $ERROR_POSITION $LAST_MATCH_INFO
-
$LAST_PAREN_MATCH $LAST_READ_LINE $MATCH $POSTMATCH $PREMATCH)
-
-
1
def initialize(opts, _pry_)
-
super(_pry_)
-
@default_switch = opts[:globals]
-
end
-
-
1
def output_self
-
variables = format(@target.eval('global_variables'))
-
output_section('global variables', grep.regexp[variables])
-
end
-
-
1
private
-
-
1
def format(globals)
-
globals.map(&:to_s).sort_by(&:downcase).map do |name|
-
if PSEUDO_GLOBALS.include?(name)
-
color(:pseudo_global, name)
-
elsif BUILTIN_GLOBALS.include?(name)
-
color(:builtin_global, name)
-
else
-
color(:global_var, name)
-
end
-
end
-
end
-
-
end
-
end
-
end
-
1
class Pry
-
1
class Command::Ls < Pry::ClassCommand
-
1
class Grep
-
-
1
def initialize(grep_regexp)
-
@grep_regexp = grep_regexp
-
end
-
-
1
def regexp
-
proc { |x|
-
if x.instance_of?(Array)
-
x.grep(@grep_regexp)
-
else
-
x =~ @grep_regexp
-
end
-
}
-
end
-
-
end
-
end
-
end
-
1
require 'pry/commands/ls/interrogatable'
-
-
1
class Pry
-
1
class Command::Ls < Pry::ClassCommand
-
1
class InstanceVars < Pry::Command::Ls::Formatter
-
1
include Pry::Command::Ls::Interrogatable
-
-
1
def initialize(interrogatee, no_user_opts, opts, _pry_)
-
super(_pry_)
-
@interrogatee = interrogatee
-
@no_user_opts = no_user_opts
-
@default_switch = opts[:ivars]
-
end
-
-
1
def correct_opts?
-
super || @no_user_opts
-
end
-
-
1
def output_self
-
ivars = if Object === @interrogatee
-
Pry::Method.safe_send(@interrogatee, :instance_variables)
-
else
-
[] #TODO: BasicObject support
-
end
-
kvars = Pry::Method.safe_send(interrogatee_mod, :class_variables)
-
ivars_out = output_section('instance variables', format(:instance_var, ivars))
-
kvars_out = output_section('class variables', format(:class_var, kvars))
-
ivars_out + kvars_out
-
end
-
-
1
private
-
-
1
def format(type, vars)
-
vars.sort_by { |var| var.to_s.downcase }.map { |var| color(type, var) }
-
end
-
-
end
-
end
-
end
-
1
module Pry::Command::Ls::Interrogatable
-
-
1
private
-
-
1
def interrogating_a_module?
-
Module === @interrogatee
-
end
-
-
1
def interrogatee_mod
-
if interrogating_a_module?
-
@interrogatee
-
else
-
singleton = Pry::Method.singleton_class_of(@interrogatee)
-
singleton.ancestors.grep(::Class).reject { |c| c == singleton }.first
-
end
-
end
-
-
end
-
1
module Pry::Command::Ls::JRubyHacks
-
-
1
private
-
-
# JRuby creates lots of aliases for methods imported from java in an attempt
-
# to make life easier for ruby programmers. (e.g. getFooBar becomes
-
# get_foo_bar and foo_bar, and maybe foo_bar? if it returns a Boolean). The
-
# full transformations are in the assignAliases method of:
-
# https://github.com/jruby/jruby/blob/master/src/org/jruby/javasupport/JavaClass.java
-
#
-
# This has the unfortunate side-effect of making the output of ls even more
-
# incredibly verbose than it normally would be for these objects; and so we
-
# filter out all but the nicest of these aliases here.
-
#
-
# TODO: This is a little bit vague, better heuristics could be used.
-
# JRuby also has a lot of scala-specific logic, which we don't copy.
-
1
def trim_jruby_aliases(methods)
-
grouped = methods.group_by do |m|
-
m.name.sub(/\A(is|get|set)(?=[A-Z_])/, '').gsub(/[_?=]/, '').downcase
-
end
-
-
grouped.map do |key, values|
-
values = values.sort_by do |m|
-
rubbishness(m.name)
-
end
-
-
found = []
-
values.select do |x|
-
(!found.any? { |y| x == y }) && found << x
-
end
-
end.flatten(1)
-
end
-
-
# When removing jruby aliases, we want to keep the alias that is
-
# "least rubbish" according to this metric.
-
1
def rubbishness(name)
-
name.each_char.map { |x|
-
case x
-
when /[A-Z]/
-
1
-
when '?', '=', '!'
-
-2
-
else
-
0
-
end
-
}.inject(&:+) + (name.size / 100.0)
-
end
-
-
end
-
1
class Pry
-
1
class Command::Ls < Pry::ClassCommand
-
1
class LocalNames < Pry::Command::Ls::Formatter
-
-
1
def initialize(no_user_opts, args, _pry_)
-
super(_pry_)
-
@no_user_opts = no_user_opts
-
@args = args
-
@sticky_locals = _pry_.sticky_locals
-
end
-
-
1
def correct_opts?
-
super || (@no_user_opts && @args.empty?)
-
end
-
-
1
def output_self
-
local_vars = grep.regexp[@target.eval('local_variables')]
-
output_section('locals', format(local_vars))
-
end
-
-
1
private
-
-
1
def format(locals)
-
locals.sort_by(&:downcase).map do |name|
-
if @sticky_locals.include?(name.to_sym)
-
color(:pry_var, name)
-
else
-
color(:local_var, name)
-
end
-
end
-
end
-
-
end
-
end
-
end
-
1
class Pry
-
1
class Command::Ls < Pry::ClassCommand
-
1
class LocalVars < Pry::Command::Ls::Formatter
-
-
1
def initialize(opts, _pry_)
-
super(_pry_)
-
@default_switch = opts[:locals]
-
@sticky_locals = _pry_.sticky_locals
-
end
-
-
1
def output_self
-
name_value_pairs = @target.eval('local_variables').reject { |e|
-
@sticky_locals.keys.include?(e.to_sym)
-
}.map { |name|
-
[name, (@target.eval(name.to_s))]
-
}
-
format(name_value_pairs).join('')
-
end
-
-
1
private
-
-
1
def format(name_value_pairs)
-
name_value_pairs.sort_by { |name, value|
-
value.to_s.size
-
}.reverse.map { |name, value|
-
colorized_assignment_style(name, format_value(value))
-
}
-
end
-
-
1
def colorized_assignment_style(lhs, rhs, desired_width = 7)
-
colorized_lhs = color(:local_var, lhs)
-
color_escape_padding = colorized_lhs.size - lhs.size
-
pad = desired_width + color_escape_padding
-
"%-#{pad}s = %s" % [color(:local_var, colorized_lhs), rhs]
-
end
-
-
end
-
end
-
end
-
1
require 'pry/commands/ls/grep'
-
1
require 'pry/commands/ls/formatter'
-
1
require 'pry/commands/ls/globals'
-
1
require 'pry/commands/ls/constants'
-
1
require 'pry/commands/ls/methods'
-
1
require 'pry/commands/ls/self_methods'
-
1
require 'pry/commands/ls/instance_vars'
-
1
require 'pry/commands/ls/local_names'
-
1
require 'pry/commands/ls/local_vars'
-
-
1
class Pry
-
1
class Command::Ls < Pry::ClassCommand
-
-
1
class LsEntity
-
1
attr_reader :_pry_
-
-
1
def initialize(opts)
-
@interrogatee = opts[:interrogatee]
-
@no_user_opts = opts[:no_user_opts]
-
@opts = opts[:opts]
-
@args = opts[:args]
-
@grep = Grep.new(Regexp.new(opts[:opts][:G] || '.'))
-
@_pry_ = opts.delete(:_pry_)
-
end
-
-
1
def entities_table
-
entities.map(&:write_out).reject { |o| !o }.join('')
-
end
-
-
1
private
-
-
1
def grep(entity)
-
entity.tap { |o| o.grep = @grep }
-
end
-
-
1
def globals
-
grep Globals.new(@opts, _pry_)
-
end
-
-
1
def constants
-
grep Constants.new(@interrogatee, @no_user_opts, @opts, _pry_)
-
end
-
-
1
def methods
-
grep(Methods.new(@interrogatee, @no_user_opts, @opts, _pry_))
-
end
-
-
1
def self_methods
-
grep SelfMethods.new(@interrogatee, @no_user_opts, @opts, _pry_)
-
end
-
-
1
def instance_vars
-
grep InstanceVars.new(@interrogatee, @no_user_opts, @opts, _pry_)
-
end
-
-
1
def local_names
-
grep LocalNames.new(@no_user_opts, @args, _pry_)
-
end
-
-
1
def local_vars
-
LocalVars.new(@opts, _pry_)
-
end
-
-
1
def entities
-
[globals, constants, methods, self_methods, instance_vars, local_names,
-
local_vars]
-
end
-
end
-
end
-
end
-
1
require 'pry/commands/ls/methods_helper'
-
1
require 'pry/commands/ls/interrogatable'
-
-
1
class Pry
-
1
class Command::Ls < Pry::ClassCommand
-
1
class Methods < Pry::Command::Ls::Formatter
-
-
1
include Pry::Command::Ls::Interrogatable
-
1
include Pry::Command::Ls::MethodsHelper
-
-
1
def initialize(interrogatee, no_user_opts, opts, _pry_)
-
super(_pry_)
-
@interrogatee = interrogatee
-
@no_user_opts = no_user_opts
-
@default_switch = opts[:methods]
-
@instance_methods_switch = opts['instance-methods']
-
@ppp_switch = opts[:ppp]
-
@jruby_switch = opts['all-java']
-
@quiet_switch = opts[:quiet]
-
@verbose_switch = opts[:verbose]
-
end
-
-
1
def output_self
-
methods = all_methods.group_by(&:owner)
-
# Reverse the resolution order so that the most useful information
-
# appears right by the prompt.
-
resolution_order.take_while(&below_ceiling).reverse.map do |klass|
-
methods_here = (methods[klass] || []).select { |m| grep.regexp[m.name] }
-
heading = "#{ Pry::WrappedModule.new(klass).method_prefix }methods"
-
output_section(heading, format(methods_here))
-
end.join('')
-
end
-
-
1
private
-
-
1
def correct_opts?
-
super || @instance_methods_switch || @ppp_switch || @no_user_opts
-
end
-
-
-
# Get a lambda that can be used with `take_while` to prevent over-eager
-
# traversal of the Object's ancestry graph.
-
1
def below_ceiling
-
ceiling = if @quiet_switch
-
[Pry::Method.safe_send(interrogatee_mod, :ancestors)[1]] +
-
_pry_.config.ls.ceiling
-
elsif @verbose_switch
-
[]
-
else
-
_pry_.config.ls.ceiling.dup
-
end
-
lambda { |klass| !ceiling.include?(klass) }
-
end
-
-
end
-
end
-
end
-
1
require 'pry/commands/ls/jruby_hacks'
-
-
1
module Pry::Command::Ls::MethodsHelper
-
-
1
include Pry::Command::Ls::JRubyHacks
-
-
1
private
-
-
# Get all the methods that we'll want to output.
-
1
def all_methods(instance_methods = false)
-
methods = if instance_methods || @instance_methods_switch
-
Pry::Method.all_from_class(@interrogatee)
-
else
-
Pry::Method.all_from_obj(@interrogatee)
-
end
-
-
if Pry::Helpers::BaseHelpers.jruby? && !@jruby_switch
-
methods = trim_jruby_aliases(methods)
-
end
-
-
methods.select { |method| @ppp_switch || method.visibility == :public }
-
end
-
-
1
def resolution_order
-
if @instance_methods_switch
-
Pry::Method.instance_resolution_order(@interrogatee)
-
else
-
Pry::Method.resolution_order(@interrogatee)
-
end
-
end
-
-
1
def format(methods)
-
methods.sort_by(&:name).map do |method|
-
if method.name == 'method_missing'
-
color(:method_missing, 'method_missing')
-
elsif method.visibility == :private
-
color(:private_method, method.name)
-
elsif method.visibility == :protected
-
color(:protected_method, method.name)
-
else
-
color(:public_method, method.name)
-
end
-
end
-
end
-
-
end
-
1
require 'pry/commands/ls/interrogatable'
-
1
require 'pry/commands/ls/methods_helper'
-
-
1
class Pry
-
1
class Command::Ls < Pry::ClassCommand
-
1
class SelfMethods < Pry::Command::Ls::Formatter
-
1
include Pry::Command::Ls::Interrogatable
-
1
include Pry::Command::Ls::MethodsHelper
-
-
1
def initialize(interrogatee, no_user_opts, opts, _pry_)
-
super(_pry_)
-
@interrogatee = interrogatee
-
@no_user_opts = no_user_opts
-
end
-
-
1
def output_self
-
methods = all_methods(true).select do |m|
-
m.owner == @interrogatee && grep.regexp[m.name]
-
end
-
heading = "#{ Pry::WrappedModule.new(@interrogatee).method_prefix }methods"
-
output_section(heading, format(methods))
-
end
-
-
1
private
-
-
1
def correct_opts?
-
@no_user_opts && interrogating_a_module?
-
end
-
-
end
-
end
-
end
-
1
class Pry
-
1
class Command::Nesting < Pry::ClassCommand
-
1
match 'nesting'
-
1
group 'Navigating Pry'
-
1
description 'Show nesting information.'
-
-
1
banner <<-'BANNER'
-
Show nesting information.
-
BANNER
-
-
1
def process
-
output.puts 'Nesting status:'
-
output.puts '--'
-
_pry_.binding_stack.each_with_index do |obj, level|
-
if level == 0
-
output.puts "#{level}. #{Pry.view_clip(obj.eval('self'))} (Pry top level)"
-
else
-
output.puts "#{level}. #{Pry.view_clip(obj.eval('self'))}"
-
end
-
end
-
end
-
end
-
-
1
Pry::Commands.add_command(Pry::Command::Nesting)
-
end
-
1
class Pry
-
1
class Command::Play < Pry::ClassCommand
-
1
match 'play'
-
1
group 'Editing'
-
1
description 'Playback a string variable, method, line, or file as input.'
-
-
1
banner <<-'BANNER'
-
Usage: play [OPTIONS] [--help]
-
-
The play command enables you to replay code from files and methods as if they
-
were entered directly in the Pry REPL.
-
-
play --lines 149..153 # assumes current context
-
play -i 20 --lines 1..3 # assumes lines of the input expression at 20
-
play -o 4 # the output of of an expression at 4
-
play Pry#repl -l 1..-1 # play the contents of Pry#repl method
-
play -e 2 # play from specified line until end of valid expression
-
play hello.rb # play a file
-
play Rakefile -l 5 # play line 5 of a file
-
play -d hi # play documentation of hi method
-
play hi --open # play hi method and leave it open
-
-
https://github.com/pry/pry/wiki/User-Input#wiki-Play
-
BANNER
-
-
1
def options(opt)
-
CodeCollector.inject_options(opt)
-
-
opt.on :open, 'Plays the selected content except the last line. Useful' \
-
' for replaying methods and leaving the method definition' \
-
' "open". `amend-line` can then be used to' \
-
' modify the method.'
-
-
opt.on :e, :expression=, 'Executes until end of valid expression', :as => Integer
-
opt.on :p, :print, 'Prints executed code'
-
end
-
-
1
def process
-
@cc = CodeCollector.new(args, opts, _pry_)
-
-
perform_play
-
show_input
-
end
-
-
1
def perform_play
-
eval_string << content_after_options
-
run "fix-indent"
-
end
-
-
1
def show_input
-
if opts.present?(:print) or !Pry::Code.complete_expression?(eval_string)
-
run "show-input"
-
end
-
end
-
-
-
1
def content_after_options
-
if opts.present?(:open)
-
restrict_to_lines(content, (0..-2))
-
elsif opts.present?(:expression)
-
content_at_expression
-
else
-
content
-
end
-
end
-
-
1
def content_at_expression
-
code_object.expression_at(opts[:expression])
-
end
-
-
1
def code_object
-
Pry::Code.new(content)
-
end
-
-
1
def should_use_default_file?
-
!args.first && !opts.present?(:in) && !opts.present?(:out)
-
end
-
-
1
def content
-
if should_use_default_file?
-
file_content
-
else
-
@cc.content
-
end
-
end
-
-
# The file to play from when no code object is specified.
-
# e.g `play --lines 4..10`
-
1
def default_file
-
target.eval("__FILE__") && File.expand_path(target.eval("__FILE__"))
-
end
-
-
1
def file_content
-
if default_file && File.exists?(default_file)
-
@cc.restrict_to_lines(File.read(default_file), @cc.line_range)
-
else
-
raise CommandError, "File does not exist! File was: #{default_file.inspect}"
-
end
-
end
-
end
-
-
1
Pry::Commands.add_command(Pry::Command::Play)
-
end
-
1
class Pry
-
1
class Command::PryBacktrace < Pry::ClassCommand
-
1
match 'pry-backtrace'
-
1
group 'Context'
-
1
description 'Show the backtrace for the Pry session.'
-
-
1
banner <<-BANNER
-
Usage: pry-backtrace [OPTIONS] [--help]
-
-
Show the backtrace for the position in the code where Pry was started. This can
-
be used to infer the behavior of the program immediately before it entered Pry,
-
just like the backtrace property of an exception.
-
-
NOTE: if you are looking for the backtrace of the most recent exception raised,
-
just type: `_ex_.backtrace` instead.
-
See: https://github.com/pry/pry/wiki/Special-Locals
-
BANNER
-
-
1
def process
-
_pry_.pager.page text.bold('Backtrace:') << "\n--\n" << _pry_.backtrace.join("\n")
-
end
-
end
-
-
1
Pry::Commands.add_command(Pry::Command::PryBacktrace)
-
end
-
1
class Pry
-
1
class Command::Version < Pry::ClassCommand
-
1
match 'pry-version'
-
1
group 'Misc'
-
1
description 'Show Pry version.'
-
-
1
banner <<-'BANNER'
-
Show Pry version.
-
BANNER
-
-
1
def process
-
output.puts "Pry version: #{Pry::VERSION} on Ruby #{RUBY_VERSION}."
-
end
-
end
-
-
1
Pry::Commands.add_command(Pry::Command::Version)
-
end
-
1
class Pry
-
# N.B. using a regular expresion here so that "raise-up 'foo'" does the right thing.
-
1
class Command::RaiseUp < Pry::ClassCommand
-
1
match(/raise-up(!?\b.*)/)
-
1
group 'Context'
-
1
description 'Raise an exception out of the current pry instance.'
-
1
command_options :listing => 'raise-up'
-
-
1
banner <<-BANNER
-
Raise up, like exit, allows you to quit pry. Instead of returning a value
-
however, it raises an exception. If you don't provide the exception to be
-
raised, it will use the most recent exception (in pry `_ex_`).
-
-
When called as raise-up! (with an exclamation mark), this command raises the
-
exception through any nested prys you have created by "cd"ing into objects.
-
-
raise-up "get-me-out-of-here"
-
-
# This is equivalent to the command above.
-
raise "get-me-out-of-here"
-
raise-up
-
BANNER
-
-
1
def process
-
return _pry.pager.page help if captures[0] =~ /(-h|--help)\b/
-
# Handle 'raise-up', 'raise-up "foo"', 'raise-up RuntimeError, 'farble' in a rubyesque manner
-
target.eval("_pry_.raise_up#{captures[0]}")
-
end
-
end
-
-
1
Pry::Commands.add_command(Pry::Command::RaiseUp)
-
end
-
1
class Pry
-
1
class Command::ReloadCode < Pry::ClassCommand
-
1
match 'reload-code'
-
1
group 'Misc'
-
1
description 'Reload the source file that contains the specified code object.'
-
-
1
banner <<-'BANNER'
-
Reload the source file that contains the specified code object.
-
-
e.g reload-code MyClass#my_method #=> reload a method
-
reload-code MyClass #=> reload a class
-
reload-code my-command #=> reload a pry command
-
reload-code self #=> reload the current object
-
reload-code #=> reload the current file or object
-
BANNER
-
-
1
def process
-
if !args.empty?
-
reload_object(args.join(" "))
-
elsif internal_binding?(target)
-
reload_object("self")
-
else
-
reload_current_file
-
end
-
end
-
-
1
private
-
-
1
def current_file
-
File.expand_path target.eval("__FILE__")
-
end
-
-
1
def reload_current_file
-
if !File.exists?(current_file)
-
raise CommandError, "Current file: #{current_file} cannot be found on disk!"
-
end
-
-
load current_file
-
output.puts "The current file: #{current_file} was reloaded!"
-
end
-
-
1
def reload_object(identifier)
-
code_object = Pry::CodeObject.lookup(identifier, _pry_)
-
check_for_reloadability(code_object, identifier)
-
load code_object.source_file
-
output.puts "#{identifier} was reloaded!"
-
end
-
-
1
def check_for_reloadability(code_object, identifier)
-
if !code_object || !code_object.source_file
-
raise CommandError, "Cannot locate #{identifier}!"
-
elsif !File.exists?(code_object.source_file)
-
raise CommandError,
-
"Cannot reload #{identifier} as it has no associated file on disk. " \
-
"File found was: #{code_object.source_file}"
-
end
-
end
-
end
-
-
1
Pry::Commands.add_command(Pry::Command::ReloadCode)
-
1
Pry::Commands.alias_command 'reload-method', 'reload-code'
-
end
-
1
class Pry
-
1
class Command::Reset < Pry::ClassCommand
-
1
match 'reset'
-
1
group 'Context'
-
1
description 'Reset the REPL to a clean state.'
-
-
1
banner <<-'BANNER'
-
Reset the REPL to a clean state.
-
BANNER
-
-
1
def process
-
output.puts 'Pry reset.'
-
exec 'pry'
-
end
-
end
-
-
1
Pry::Commands.add_command(Pry::Command::Reset)
-
end
-
1
class Pry
-
1
class Command::Ri < Pry::ClassCommand
-
1
match 'ri'
-
1
group 'Introspection'
-
1
description 'View ri documentation.'
-
-
1
banner <<-'BANNER'
-
Usage: ri [spec]
-
-
View ri documentation. Relies on the "rdoc" gem being installed.
-
See also "show-doc" command.
-
-
ri Array#each
-
BANNER
-
-
1
def process(spec)
-
# Lazily load RI
-
require 'rdoc/ri/driver'
-
-
unless defined? RDoc::RI::PryDriver
-
-
# Subclass RI so that it formats its output nicely, and uses `lesspipe`.
-
subclass = Class.new(RDoc::RI::Driver) # the hard way.
-
-
subclass.class_eval do
-
def initialize(pager, opts)
-
@pager = pager
-
super opts
-
end
-
def page
-
paging_text = StringIO.new
-
yield paging_text
-
@pager.page(paging_text.string)
-
end
-
-
def formatter(io)
-
if @formatter_klass
-
@formatter_klass.new
-
else
-
RDoc::Markup::ToAnsi.new
-
end
-
end
-
end
-
-
RDoc::RI.const_set :PryDriver, subclass # hook it up!
-
end
-
-
# Spin-up an RI insance.
-
ri = RDoc::RI::PryDriver.new _pry_.pager, :use_stdout => true, :interactive => false
-
-
begin
-
ri.display_names [spec] # Get the documentation (finally!)
-
rescue RDoc::RI::Driver::NotFoundError => e
-
output.puts "error: '#{e.name}' not found"
-
end
-
end
-
end
-
-
1
Pry::Commands.add_command(Pry::Command::Ri)
-
end
-
1
require 'pry/commands/code_collector'
-
-
1
class Pry
-
1
class Command::SaveFile < Pry::ClassCommand
-
1
match 'save-file'
-
1
group 'Input and Output'
-
1
description 'Export to a file using content from the REPL.'
-
-
1
banner <<-'BANNER'
-
Usage: save-file [OPTIONS] --to [FILE]
-
-
Export to a file using content from the REPL.
-
-
save-file my_method --to hello.rb
-
save-file -i 1..10 --to hello.rb --append
-
save-file show-method --to my_command.rb
-
save-file sample_file.rb --lines 2..10 --to output_file.rb
-
BANNER
-
-
1
def options(opt)
-
CodeCollector.inject_options(opt)
-
-
opt.on :to=, "Specify the output file path"
-
opt.on :a, :append, "Append output to file"
-
end
-
-
1
def process
-
@cc = CodeCollector.new(args, opts, _pry_)
-
raise CommandError, "Found no code to save." if @cc.content.empty?
-
-
if !file_name
-
display_content
-
else
-
save_file
-
end
-
end
-
-
1
def file_name
-
opts[:to] || nil
-
end
-
-
1
def save_file
-
File.open(file_name, mode) do |f|
-
f.puts @cc.content
-
end
-
output.puts "#{file_name} successfully saved"
-
end
-
-
1
def display_content
-
output.puts @cc.content
-
output.puts "\n\n--\nPlease use `--to FILE` to export to a file."
-
output.puts "No file saved!\n--"
-
end
-
-
1
def mode
-
opts.present?(:append) ? "a" : "w"
-
end
-
end
-
-
1
Pry::Commands.add_command(Pry::Command::SaveFile)
-
end
-
1
class Pry
-
1
class Command::ShellCommand < Pry::ClassCommand
-
1
match(/\.(.*)/)
-
1
group 'Input and Output'
-
1
description "All text following a '.' is forwarded to the shell."
-
1
command_options :listing => '.<shell command>', :use_prefix => false,
-
:takes_block => true
-
-
1
banner <<-'BANNER'
-
Usage: .COMMAND_NAME
-
-
All text following a "." is forwarded to the shell.
-
-
.ls -aF
-
.uname
-
BANNER
-
-
1
def process(cmd)
-
if cmd =~ /^cd\s*(.*)/i
-
process_cd parse_destination($1)
-
else
-
pass_block(cmd)
-
if command_block
-
command_block.call `#{cmd}`
-
else
-
_pry_.config.system.call(output, cmd, _pry_)
-
end
-
end
-
end
-
-
1
private
-
-
1
def parse_destination(dest)
-
return "~" if dest.empty?
-
return dest unless dest == "-"
-
state.old_pwd || raise(CommandError, "No prior directory available")
-
end
-
-
1
def process_cd(dest)
-
state.old_pwd = Dir.pwd
-
Dir.chdir File.expand_path(dest)
-
rescue Errno::ENOENT
-
raise CommandError, "No such directory: #{dest}"
-
end
-
end
-
-
1
Pry::Commands.add_command(Pry::Command::ShellCommand)
-
end
-
1
class Pry
-
1
class Command::ShellMode < Pry::ClassCommand
-
1
match 'shell-mode'
-
1
group 'Input and Output'
-
1
description 'Toggle shell mode. Bring in pwd prompt and file completion.'
-
-
1
banner <<-'BANNER'
-
Toggle shell mode. Bring in pwd prompt and file completion.
-
BANNER
-
-
1
def process
-
case _pry_.prompt
-
when Pry::SHELL_PROMPT
-
_pry_.pop_prompt
-
_pry_.custom_completions = _pry_.config.file_completions
-
else
-
_pry_.push_prompt Pry::SHELL_PROMPT
-
_pry_.custom_completions = _pry_.config.command_completions
-
end
-
end
-
end
-
-
1
Pry::Commands.add_command(Pry::Command::ShellMode)
-
1
Pry::Commands.alias_command 'file-mode', 'shell-mode'
-
end
-
1
require 'pry/commands/show_info'
-
-
1
class Pry
-
1
class Command::ShowDoc < Command::ShowInfo
-
1
include Pry::Helpers::DocumentationHelpers
-
-
1
match 'show-doc'
-
1
group 'Introspection'
-
1
description 'Show the documentation for a method or class.'
-
-
1
banner <<-BANNER
-
Usage: show-doc [OPTIONS] [METH]
-
Aliases: ?
-
-
Show the documentation for a method or class. Tries instance methods first and
-
then methods by default.
-
-
show-doc hi_method # docs for hi_method
-
show-doc Pry # for Pry class
-
show-doc Pry -a # for all definitions of Pry class (all monkey patches)
-
BANNER
-
-
# The docs for code_object prepared for display.
-
1
def content_for(code_object)
-
Code.new(render_doc_markup_for(code_object),
-
start_line_for(code_object), :text).
-
with_line_numbers(use_line_numbers?).to_s
-
end
-
-
# process the markup (if necessary) and apply colors
-
1
def render_doc_markup_for(code_object)
-
docs = docs_for(code_object)
-
-
if code_object.command?
-
# command '--help' shouldn't use markup highlighting
-
docs
-
else
-
if docs.empty?
-
raise CommandError, "No docs found for: #{
-
obj_name ? obj_name : 'current context'
-
}"
-
end
-
process_comment_markup(docs)
-
end
-
end
-
-
# Return docs for the code_object, adjusting for whether the code_object
-
# has yard docs available, in which case it returns those.
-
# (note we only have to check yard docs for modules since they can
-
# have multiple docs, but methods can only be doc'd once so we
-
# dont need to check them)
-
1
def docs_for(code_object)
-
if code_object.module_with_yard_docs?
-
# yard docs
-
code_object.yard_doc
-
else
-
# normal docs (i.e comments above method/module/command)
-
code_object.doc
-
end
-
end
-
-
# Which sections to include in the 'header', can toggle: :owner,
-
# :signature and visibility.
-
1
def header_options
-
super.merge :signature => true
-
end
-
-
# figure out start line of docs by back-calculating based on
-
# number of lines in the comment and the start line of the code_object
-
# @return [Fixnum] start line of docs
-
1
def start_line_for(code_object)
-
if code_object.command? || opts.present?(:'base-one')
-
1
-
else
-
code_object.source_line.nil? ? 1 :
-
(code_object.source_line - code_object.doc.lines.count)
-
end
-
end
-
end
-
-
1
Pry::Commands.add_command(Pry::Command::ShowDoc)
-
1
Pry::Commands.alias_command '?', 'show-doc'
-
end
-
1
class Pry
-
1
class Command::ShowInfo < Pry::ClassCommand
-
1
extend Pry::Helpers::BaseHelpers
-
-
1
command_options :shellwords => false, :interpolate => false
-
-
1
def options(opt)
-
opt.on :s, :super, "Select the 'super' method. Can be repeated to traverse the ancestors", :as => :count
-
opt.on :l, "line-numbers", "Show line numbers"
-
opt.on :b, "base-one", "Show line numbers but start numbering at 1 (useful for `amend-line` and `play` commands)"
-
opt.on :a, :all, "Show all definitions and monkeypatches of the module/class"
-
end
-
-
1
def process
-
code_object = Pry::CodeObject.lookup(obj_name, _pry_, :super => opts[:super])
-
raise CommandError, no_definition_message if !code_object
-
@original_code_object = code_object
-
-
if show_all_modules?(code_object)
-
# show all monkey patches for a module
-
-
result = content_and_headers_for_all_module_candidates(code_object)
-
else
-
# show a specific code object
-
co = code_object_with_accessible_source(code_object)
-
result = content_and_header_for_code_object(co)
-
end
-
-
set_file_and_dir_locals(code_object.source_file)
-
_pry_.pager.page result
-
end
-
-
# This method checks whether the `code_object` is a WrappedModule,
-
# if it is, then it returns the first candidate (monkeypatch) with
-
# accessible source (or docs). If `code_object` is not a WrappedModule (i.e a
-
# method or a command) then the `code_object` itself is just
-
# returned.
-
#
-
# @return [Pry::WrappedModule, Pry::Method, Pry::Command]
-
1
def code_object_with_accessible_source(code_object)
-
if code_object.is_a?(WrappedModule)
-
candidate = code_object.candidates.find(&:source)
-
if candidate
-
return candidate
-
else
-
raise CommandError, no_definition_message if !valid_superclass?(code_object)
-
-
@used_super = true
-
code_object_with_accessible_source(code_object.super)
-
end
-
else
-
code_object
-
end
-
end
-
-
1
def valid_superclass?(code_object)
-
code_object.super && code_object.super.wrapped != Object
-
end
-
-
1
def content_and_header_for_code_object(code_object)
-
header(code_object) << content_for(code_object)
-
end
-
-
1
def content_and_headers_for_all_module_candidates(mod)
-
result = "Found #{mod.number_of_candidates} candidates for `#{mod.name}` definition:\n"
-
mod.number_of_candidates.times do |v|
-
candidate = mod.candidate(v)
-
begin
-
result << "\nCandidate #{v+1}/#{mod.number_of_candidates}: #{candidate.source_file} @ line #{candidate.source_line}:\n"
-
content = content_for(candidate)
-
-
result << "Number of lines: #{content.lines.count}\n\n" << content
-
rescue Pry::RescuableException
-
result << "\nNo content found.\n"
-
next
-
end
-
end
-
result
-
end
-
-
1
def no_definition_message
-
"Couldn't locate a definition for #{obj_name}!"
-
end
-
-
# Generate a header (meta-data information) for all the code
-
# object types: methods, modules, commands, procs...
-
1
def header(code_object)
-
file_name, line_num = file_and_line_for(code_object)
-
h = "\n#{Pry::Helpers::Text.bold('From:')} #{file_name} "
-
h << code_object_header(code_object, line_num)
-
h << "\n#{Pry::Helpers::Text.bold('Number of lines:')} " <<
-
"#{content_for(code_object).lines.count}\n\n"
-
h << Helpers::Text.bold('** Warning:') << " Cannot find code for #{@original_code_object.nonblank_name}. Showing superclass #{code_object.nonblank_name} instead. **\n\n" if @used_super
-
h
-
end
-
-
1
def code_object_header(code_object, line_num)
-
if code_object.real_method_object?
-
method_header(code_object, line_num)
-
-
# It sucks we have to test for both Pry::WrappedModule and WrappedModule::Candidate,
-
# probably indicates a deep refactor needs to happen in those classes.
-
elsif code_object.is_a?(Pry::WrappedModule) || code_object.is_a?(Pry::WrappedModule::Candidate)
-
module_header(code_object, line_num)
-
else
-
""
-
end
-
end
-
-
1
def method_header(code_object, line_num)
-
h = ""
-
h << (code_object.c_method? ? "(C Method):" : "@ line #{line_num}:")
-
h << method_sections(code_object)[:owner]
-
h << method_sections(code_object)[:visibility]
-
h << method_sections(code_object)[:signature]
-
h
-
end
-
-
1
def module_header(code_object, line_num)
-
h = ""
-
h << "@ line #{line_num}:\n"
-
h << text.bold(code_object.module? ? "Module" : "Class")
-
h << " #{text.bold('name:')} #{code_object.nonblank_name}"
-
-
if code_object.number_of_candidates > 1
-
h << (text.bold("\nNumber of monkeypatches: ") << code_object.number_of_candidates.to_s)
-
h << ". Use the `-a` option to display all available monkeypatches"
-
end
-
h
-
end
-
-
1
def method_sections(code_object)
-
{
-
:owner => "\n#{text.bold("Owner:")} #{code_object.owner || "N/A"}\n",
-
:visibility => "#{text.bold("Visibility:")} #{code_object.visibility}",
-
:signature => "\n#{text.bold("Signature:")} #{code_object.signature}"
-
}.merge(header_options) { |key, old, new| (new && old).to_s }
-
end
-
-
1
def header_options
-
{
-
:owner => true,
-
:visibility => true,
-
:signature => nil
-
}
-
end
-
-
1
def show_all_modules?(code_object)
-
code_object.is_a?(Pry::WrappedModule) && opts.present?(:all)
-
end
-
-
1
def obj_name
-
@obj_name ||= args.empty? ? nil : args.join(' ')
-
end
-
-
1
def use_line_numbers?
-
opts.present?(:b) || opts.present?(:l)
-
end
-
-
1
def start_line_for(code_object)
-
if opts.present?(:'base-one')
-
1
-
else
-
code_object.source_line || 1
-
end
-
end
-
-
# takes into account possible yard docs, and returns yard_file / yard_line
-
# Also adjusts for start line of comments (using start_line_for), which it has to infer
-
# by subtracting number of lines of comment from start line of code_object
-
1
def file_and_line_for(code_object)
-
if code_object.module_with_yard_docs?
-
[code_object.yard_file, code_object.yard_line]
-
else
-
[code_object.source_file, start_line_for(code_object)]
-
end
-
end
-
-
1
def complete(input)
-
if input =~ /([^ ]*)#([a-z0-9_]*)\z/
-
prefix, search = [$1, $2]
-
methods = begin
-
Pry::Method.all_from_class(binding.eval(prefix))
-
rescue RescuableException
-
return super
-
end
-
methods.map do |method|
-
[prefix, method.name].join('#') if method.name.start_with?(search)
-
end.compact
-
else
-
super
-
end
-
end
-
end
-
end
-
1
class Pry
-
1
class Command::ShowInput < Pry::ClassCommand
-
1
match 'show-input'
-
1
group 'Editing'
-
1
description 'Show the contents of the input buffer for the current multi-line expression.'
-
-
1
banner <<-'BANNER'
-
Show the contents of the input buffer for the current multi-line expression.
-
BANNER
-
-
1
def process
-
output.puts Code.new(eval_string).with_line_numbers
-
end
-
end
-
-
1
Pry::Commands.add_command(Pry::Command::ShowInput)
-
end
-
1
require 'pry/commands/show_info'
-
-
1
class Pry
-
1
class Command::ShowSource < Command::ShowInfo
-
1
match 'show-source'
-
1
group 'Introspection'
-
1
description 'Show the source for a method or class.'
-
-
1
banner <<-'BANNER'
-
Usage: show-source [OPTIONS] [METH|CLASS]
-
Aliases: $, show-method
-
-
Show the source for a method or class. Tries instance methods first and then
-
methods by default.
-
-
show-source hi_method
-
show-source hi_method
-
show-source Pry#rep # source for Pry#rep method
-
show-source Pry # for Pry class
-
show-source Pry -a # for all Pry class definitions (all monkey patches)
-
show-source Pry.foo -e # for class of the return value of expression `Pry.foo`
-
show-source Pry --super # for superclass of Pry (Object class)
-
-
https://github.com/pry/pry/wiki/Source-browsing#wiki-Show_method
-
BANNER
-
-
1
def options(opt)
-
opt.on :e, :eval, "evaluate the command's argument as a ruby expression and show the class its return value"
-
super(opt)
-
end
-
-
1
def process
-
if opts.present?(:e)
-
obj = target.eval(args.first)
-
self.args = Array.new(1) { Module === obj ? obj.name : obj.class.name }
-
end
-
super
-
end
-
-
# The source for code_object prepared for display.
-
1
def content_for(code_object)
-
Code.new(code_object.source, start_line_for(code_object)).
-
with_line_numbers(use_line_numbers?).highlighted
-
end
-
end
-
-
1
Pry::Commands.add_command(Pry::Command::ShowSource)
-
1
Pry::Commands.alias_command 'show-method', 'show-source'
-
1
Pry::Commands.alias_command '$', 'show-source'
-
end
-
1
class Pry
-
1
class Command::SimplePrompt < Pry::ClassCommand
-
1
match 'simple-prompt'
-
1
group 'prompts'
-
1
description 'Toggle the simple prompt.'
-
-
1
banner <<-'BANNER'
-
Toggle the simple prompt.
-
BANNER
-
-
1
def process
-
case _pry_.prompt
-
when Pry::SIMPLE_PROMPT
-
_pry_.pop_prompt
-
else
-
_pry_.push_prompt Pry::SIMPLE_PROMPT
-
end
-
end
-
end
-
-
1
Pry::Commands.add_command(Pry::Command::SimplePrompt)
-
end
-
1
class Pry
-
1
class Command::Stat < Pry::ClassCommand
-
1
match 'stat'
-
1
group 'Introspection'
-
1
description 'View method information and set _file_ and _dir_ locals.'
-
1
command_options :shellwords => false
-
-
1
banner <<-'BANNER'
-
Usage: stat [OPTIONS] [METH]
-
-
Show method information for method METH and set _file_ and _dir_ locals.
-
-
stat hello_method
-
BANNER
-
-
1
def options(opt)
-
method_options(opt)
-
end
-
-
1
def process
-
meth = method_object
-
aliases = meth.aliases
-
-
output.puts unindent <<-EOS
-
Method Information:
-
--
-
Name: #{meth.name}
-
Alias#{ "es" if aliases.length > 1 }: #{ aliases.any? ? aliases.join(", ") : "None." }
-
Owner: #{meth.owner ? meth.owner : "Unknown"}
-
Visibility: #{meth.visibility}
-
Type: #{meth.is_a?(::Method) ? "Bound" : "Unbound"}
-
Arity: #{meth.arity}
-
Method Signature: #{meth.signature}
-
Source Location: #{meth.source_location ? meth.source_location.join(":") : "Not found."}
-
EOS
-
end
-
end
-
-
1
Pry::Commands.add_command(Pry::Command::Stat)
-
end
-
1
class Pry
-
1
class Command::SwitchTo < Pry::ClassCommand
-
1
match 'switch-to'
-
1
group 'Navigating Pry'
-
1
description 'Start a new subsession on a binding in the current stack.'
-
-
1
banner <<-'BANNER'
-
Start a new subsession on a binding in the current stack (numbered by nesting).
-
BANNER
-
-
1
def process(selection)
-
selection = selection.to_i
-
-
if selection < 0 || selection > _pry_.binding_stack.size - 1
-
raise CommandError, "Invalid binding index #{selection} - use `nesting` command to view valid indices."
-
else
-
Pry.start(_pry_.binding_stack[selection])
-
end
-
end
-
end
-
-
1
Pry::Commands.add_command(Pry::Command::SwitchTo)
-
end
-
1
class Pry
-
1
class Command::ToggleColor < Pry::ClassCommand
-
1
match 'toggle-color'
-
1
group 'Misc'
-
1
description 'Toggle syntax highlighting.'
-
-
1
banner <<-'BANNER'
-
Usage: toggle-color
-
-
Toggle syntax highlighting.
-
BANNER
-
-
1
def process
-
_pry_.color = color_toggle
-
output.puts "Syntax highlighting #{_pry_.color ? "on" : "off"}"
-
end
-
-
1
def color_toggle
-
!_pry_.color
-
end
-
-
1
Pry::Commands.add_command(self)
-
end
-
end
-
1
class Pry
-
1
class Command::WatchExpression < Pry::ClassCommand
-
1
require 'pry/commands/watch_expression/expression.rb'
-
-
1
match 'watch'
-
1
group 'Context'
-
1
description 'Watch the value of an expression and print a notification whenever it changes.'
-
1
command_options :use_prefix => false
-
-
1
banner <<-'BANNER'
-
Usage: watch [EXPRESSION]
-
watch
-
watch --delete [INDEX]
-
-
watch [EXPRESSION] adds an expression to the list of those being watched.
-
It will be re-evaluated every time you hit enter in pry. If its value has
-
changed, the new value will be printed to the console.
-
-
This is useful if you are step-through debugging and want to see how
-
something changes over time. It's also useful if you're trying to write
-
a method inside pry and want to check that it gives the right answers
-
every time you redefine it.
-
-
watch on its own displays all the currently watched expressions and their
-
values, and watch --delete [INDEX] allows you to delete expressions from
-
the list being watched.
-
BANNER
-
-
1
def options(opt)
-
opt.on :d, :delete,
-
"Delete the watch expression with the given index. If no index is given; clear all watch expressions.",
-
:optional_argument => true, :as => Integer
-
opt.on :l, :list,
-
"Show all current watch expressions and their values. Calling watch with no expressions or options will also show the watch expressions."
-
end
-
-
1
def process
-
case
-
when opts.present?(:delete)
-
delete opts[:delete]
-
when opts.present?(:list) || args.empty?
-
list
-
else
-
add_hook
-
add_expression(args)
-
end
-
end
-
-
1
private
-
-
1
def expressions
-
_pry_.config.watch_expressions ||= []
-
end
-
-
1
def delete(index)
-
if index
-
output.puts "Deleting watch expression ##{index}: #{expressions[index-1]}"
-
expressions.delete_at(index-1)
-
else
-
output.puts "Deleting all watched expressions"
-
expressions.clear
-
end
-
end
-
-
1
def list
-
if expressions.empty?
-
output.puts "No watched expressions"
-
else
-
_pry_.pager.open do |pager|
-
pager.puts "Listing all watched expressions:"
-
pager.puts ""
-
expressions.each_with_index do |expr, index|
-
pager.print text.with_line_numbers(expr.to_s, index+1)
-
end
-
pager.puts ""
-
end
-
end
-
end
-
-
1
def eval_and_print_changed(output)
-
expressions.each do |expr|
-
expr.eval!
-
if expr.changed?
-
output.puts "#{text.blue "watch"}: #{expr.to_s}"
-
end
-
end
-
end
-
-
1
def add_expression(arguments)
-
expressions << Expression.new(_pry_, target, arg_string)
-
output.puts "Watching #{Code.new(arg_string).highlighted}"
-
end
-
-
1
def add_hook
-
hook = [:after_eval, :watch_expression]
-
unless _pry_.hooks.hook_exists?(*hook)
-
_pry_.hooks.add_hook(*hook) do |_, _pry_|
-
eval_and_print_changed _pry_.output
-
end
-
end
-
end
-
end
-
-
1
Pry::Commands.add_command(Pry::Command::WatchExpression)
-
end
-
1
class Pry
-
1
class Command::WatchExpression
-
1
class Expression
-
1
attr_reader :target, :source, :value, :previous_value, :_pry_
-
-
1
def initialize(_pry_, target, source)
-
@_pry_ = _pry_
-
@target = target
-
@source = Code.new(source).strip
-
end
-
-
1
def eval!
-
@previous_value = @value
-
@value = Pry::ColorPrinter.pp(target_eval(target, source), "")
-
end
-
-
1
def to_s
-
"#{Code.new(source).highlighted.strip} => #{value}"
-
end
-
-
# Has the value of the expression changed?
-
#
-
# We use the pretty-printed string represenation to detect differences
-
# as this avoids problems with dup (causes too many differences) and == (causes too few)
-
1
def changed?
-
(value != previous_value)
-
end
-
-
1
private
-
-
1
def target_eval(target, source)
-
target.eval(source)
-
rescue => e
-
e
-
end
-
end
-
end
-
end
-
1
class Pry
-
1
class Command::Whereami < Pry::ClassCommand
-
-
1
class << self
-
1
attr_accessor :method_size_cutoff
-
end
-
-
1
@method_size_cutoff = 30
-
-
1
match 'whereami'
-
1
description 'Show code surrounding the current context.'
-
1
group 'Context'
-
-
1
banner <<-'BANNER'
-
Usage: whereami [-qn] [LINES]
-
-
Describe the current location. If you use `binding.pry` inside a method then
-
whereami will print out the source for that method.
-
-
If a number is passed, then LINES lines before and after the current line will be
-
shown instead of the method itself.
-
-
The `-q` flag can be used to suppress error messages in the case that there's
-
no code to show. This is used by pry in the default before_session hook to show
-
you when you arrive at a `binding.pry`.
-
-
The `-n` flag can be used to hide line numbers so that code can be copy/pasted
-
effectively.
-
-
When pry was started on an Object and there is no associated method, whereami
-
will instead output a brief description of the current object.
-
BANNER
-
-
1
def setup
-
4
@file = expand_path(target.eval('__FILE__'))
-
4
@line = target.eval('__LINE__')
-
4
@method = Pry::Method.from_binding(target)
-
end
-
-
1
def options(opt)
-
4
opt.on :q, :quiet, "Don't display anything in case of an error"
-
4
opt.on :n, :"no-line-numbers", "Do not display line numbers"
-
4
opt.on :m, :"method", "Show the complete source for the current method."
-
4
opt.on :c, :"class", "Show the complete source for the current class or module."
-
4
opt.on :f, :"file", "Show the complete source for the current file."
-
end
-
-
1
def code
-
@code ||= if opts.present?(:m)
-
method_code or raise CommandError, "Cannot find method code."
-
elsif opts.present?(:c)
-
class_code or raise CommandError, "Cannot find class code."
-
elsif opts.present?(:f)
-
Pry::Code.from_file(@file)
-
elsif args.any?
-
code_window
-
else
-
4
default_code
-
8
end
-
end
-
-
1
def code?
-
4
!!code
-
rescue MethodSource::SourceNotFoundError
-
false
-
end
-
-
1
def bad_option_combination?
-
[opts.present?(:m), opts.present?(:f),
-
4
opts.present?(:c), args.any?].count(true) > 1
-
end
-
-
1
def location
-
4
"#{@file} @ line #{@line} #{@method && @method.name_with_owner}"
-
end
-
-
1
def process
-
4
if bad_option_combination?
-
raise CommandError, "Only one of -m, -c, -f, and LINES may be specified."
-
end
-
-
4
if nothing_to_do?
-
return
-
elsif internal_binding?(target)
-
handle_internal_binding
-
return
-
end
-
-
4
set_file_and_dir_locals(@file)
-
-
4
out = "\n#{text.bold('From:')} #{location}:\n\n" <<
-
code.with_line_numbers(use_line_numbers?).with_marker(marker).highlighted << "\n"
-
-
4
_pry_.pager.page out
-
end
-
-
1
private
-
-
1
def nothing_to_do?
-
4
opts.quiet? && (internal_binding?(target) || !code?)
-
end
-
-
1
def use_line_numbers?
-
4
!opts.present?(:n)
-
end
-
-
1
def marker
-
4
!opts.present?(:n) && @line
-
end
-
-
1
def top_level?
-
target_self == Pry.main
-
end
-
-
1
def handle_internal_binding
-
if top_level?
-
output.puts "At the top level."
-
else
-
output.puts "Inside #{Pry.view_clip(target_self)}."
-
end
-
end
-
-
1
def small_method?
-
4
@method.source_range.count < self.class.method_size_cutoff
-
end
-
-
1
def default_code
-
4
if method_code && small_method?
-
4
method_code
-
else
-
code_window
-
end
-
end
-
-
1
def code_window
-
Pry::Code.from_file(@file).around(@line, window_size)
-
end
-
-
1
def method_code
-
8
return @method_code if @method_code
-
-
4
if valid_method?
-
4
@method_code = Pry::Code.from_method(@method)
-
end
-
end
-
-
# This either returns the `target_self`
-
# or it returns the class of `target_self` if `target_self` is not a class.
-
# @return [Pry::WrappedModule]
-
1
def target_class
-
target_self.is_a?(Module) ? Pry::WrappedModule(target_self) :
-
Pry::WrappedModule(target_self.class)
-
end
-
-
1
def class_code
-
return @class_code if @class_code
-
-
mod = @method ? Pry::WrappedModule(@method.owner) : target_class
-
-
idx = mod.candidates.find_index { |v| expand_path(v.source_file) == @file }
-
@class_code = idx && Pry::Code.from_module(mod, idx)
-
end
-
-
1
def valid_method?
-
4
@method && @method.source? && expand_path(@method.source_file) == @file &&
-
@method.source_range.include?(@line)
-
end
-
-
1
def expand_path(f)
-
8
return if !f
-
-
8
if Pry.eval_path == f
-
2
f
-
else
-
6
File.expand_path(f)
-
end
-
end
-
-
1
def window_size
-
if args.empty?
-
_pry_.config.default_window_size
-
else
-
args.first.to_i
-
end
-
end
-
end
-
-
1
Pry::Commands.add_command(Pry::Command::Whereami)
-
1
Pry::Commands.alias_command '@', 'whereami'
-
end
-
1
class Pry
-
1
class Command::Wtf < Pry::ClassCommand
-
1
match(/wtf([?!]*)/)
-
1
group 'Context'
-
1
description 'Show the backtrace of the most recent exception.'
-
1
options :listing => 'wtf?'
-
-
1
banner <<-'BANNER'
-
Usage: wtf[?|!]
-
-
Show's a few lines of the backtrace of the most recent exception (also available
-
as `_ex_.backtrace`). If you want to see more lines, add more question marks or
-
exclamation marks.
-
-
wtf?
-
wtf?!???!?!?
-
-
# To see the entire backtrace, pass the `-v` or `--verbose` flag.
-
wtf -v
-
BANNER
-
-
1
def options(opt)
-
opt.on :v, :verbose, "Show the full backtrace"
-
end
-
-
1
def process
-
raise Pry::CommandError, "No most-recent exception" unless exception
-
-
output.puts "#{text.bold('Exception:')} #{exception.class}: #{exception}\n--"
-
if opts.verbose?
-
output.puts with_line_numbers(backtrace)
-
else
-
output.puts with_line_numbers(backtrace.first(size_of_backtrace))
-
end
-
end
-
-
1
private
-
-
1
def exception
-
_pry_.last_exception
-
end
-
-
1
def with_line_numbers(bt)
-
Pry::Code.new(bt, 0, :text).with_line_numbers.to_s
-
end
-
-
1
def backtrace
-
exception.backtrace
-
end
-
-
1
def size_of_backtrace
-
[captures[0].size, 0.5].max * 10
-
end
-
end
-
-
1
Pry::Commands.add_command(Pry::Command::Wtf)
-
end
-
1
class Pry::Config
-
1
require_relative 'config/behavior'
-
1
require_relative 'config/default'
-
1
require_relative 'config/convenience'
-
1
include Pry::Config::Behavior
-
-
1
def self.shortcuts
-
2
Convenience::SHORTCUTS
-
end
-
-
#
-
# FIXME
-
# @param [Pry::Hooks] hooks
-
#
-
1
def hooks=(hooks)
-
if hooks.is_a?(Hash)
-
warn "Hash-based hooks are now deprecated! Use a `Pry::Hooks` object " \
-
"instead! http://rubydoc.info/github/pry/pry/master/Pry/Hooks"
-
self["hooks"] = Pry::Hooks.from_hash(hooks)
-
else
-
self["hooks"] = hooks
-
end
-
end
-
end
-
1
module Pry::Config::Behavior
-
1
ASSIGNMENT = "=".freeze
-
1
NODUP = [TrueClass, FalseClass, NilClass, Symbol, Numeric, Module, Proc].freeze
-
1
INSPECT_REGEXP = /#{Regexp.escape "default=#<"}/
-
-
1
module Builder
-
1
def from_hash(hash, default = nil)
-
27
new(default).tap do |config|
-
27
config.merge!(hash)
-
end
-
end
-
end
-
-
1
def self.included(klass)
-
2
unless defined?(RESERVED_KEYS)
-
1
const_set :RESERVED_KEYS, instance_methods(false).map(&:to_s).freeze
-
end
-
2
klass.extend(Builder)
-
end
-
-
1
def initialize(default = Pry.config)
-
33
@default = default
-
33
@lookup = {}
-
end
-
-
#
-
# @return [Pry::Config::Behavior]
-
# returns the default used if a matching value for a key isn't found in self
-
#
-
1
def default
-
@default
-
end
-
-
1
def [](key)
-
271
@lookup[key.to_s]
-
end
-
-
1
def []=(key, value)
-
299
key = key.to_s
-
299
if RESERVED_KEYS.include?(key)
-
raise ArgumentError, "few things are reserved by pry, but using '#{key}' as a configuration key is."
-
end
-
299
@lookup[key] = value
-
end
-
-
1
def method_missing(name, *args, &block)
-
2880
key = name.to_s
-
2880
if key[-1] == ASSIGNMENT
-
1
short_key = key[0..-2]
-
1
self[short_key] = args[0]
-
2879
elsif key?(key)
-
271
self[key]
-
2608
elsif @default.respond_to?(name)
-
2608
value = @default.public_send(name, *args, &block)
-
# FIXME: refactor Pry::Hook so that it stores config on the config object,
-
# so that we can use the normal strategy.
-
2608
self[key] = value = value.dup if key == 'hooks'
-
2608
value
-
else
-
nil
-
end
-
end
-
-
1
def merge!(other)
-
31
other = try_convert_to_hash(other)
-
31
raise TypeError, "unable to convert argument into a Hash" unless other
-
31
other.each do |key, value|
-
267
self[key] = value
-
end
-
end
-
-
1
def ==(other)
-
@lookup == try_convert_to_hash(other)
-
end
-
1
alias_method :eql?, :==
-
-
1
def respond_to_missing?(key, include_private=false)
-
343
key?(key) or @default.respond_to?(key) or super(key, include_private)
-
end
-
-
1
def key?(key)
-
3222
key = key.to_s
-
3222
@lookup.key?(key)
-
end
-
-
1
def clear
-
@lookup.clear
-
true
-
end
-
1
alias_method :refresh, :clear
-
-
1
def forget(key)
-
@lookup.delete(key.to_s)
-
end
-
-
1
def keys
-
@lookup.keys
-
end
-
-
1
def to_hash
-
@lookup.dup
-
end
-
1
alias_method :to_h, :to_hash
-
-
-
1
def inspect
-
key_str = keys.map { |key| "'#{key}'" }.join(",")
-
"#<#{_clip_inspect(self)} local_keys=[#{key_str}] default=#{@default.inspect}>"
-
end
-
-
1
def pretty_print(q)
-
q.text inspect[1..-1].gsub(INSPECT_REGEXP, "default=<")
-
end
-
-
1
private
-
1
def _clip_inspect(obj)
-
"#{obj.class}:0x%x" % obj.object_id << 1
-
end
-
-
1
def _dup(value)
-
if NODUP.any? { |klass| klass === value }
-
value
-
else
-
value.dup
-
end
-
end
-
-
1
def try_convert_to_hash(obj)
-
31
if Hash === obj
-
31
obj
-
elsif obj.respond_to?(:to_h)
-
obj.to_h
-
elsif obj.respond_to?(:to_hash)
-
obj.to_hash
-
else
-
nil
-
end
-
end
-
end
-
1
module Pry::Config::Convenience
-
1
SHORTCUTS = [
-
:input,
-
:output,
-
:commands,
-
:print,
-
:exception_handler,
-
:hooks,
-
:color,
-
:pager,
-
:editor,
-
:memory_size,
-
:extra_sticky_locals
-
]
-
-
-
1
def config_shortcut(*names)
-
2
names.each do |name|
-
22
reader = name
-
22
setter = "#{name}="
-
316
define_method(reader) { config.public_send(name) }
-
22
define_method(setter) { |value| config.public_send(setter, value) }
-
end
-
end
-
end
-
1
class Pry::Config::Default
-
1
include Pry::Config::Behavior
-
-
1
default = {
-
input: proc {
-
1
lazy_readline
-
},
-
output: proc {
-
1
$stdout
-
},
-
commands: proc {
-
1
Pry::Commands
-
},
-
prompt_name: proc {
-
1
Pry::DEFAULT_PROMPT_NAME
-
},
-
prompt: proc {
-
1
Pry::DEFAULT_PROMPT
-
},
-
prompt_safe_objects: proc {
-
1
Pry::DEFAULT_PROMPT_SAFE_OBJECTS
-
},
-
print: proc {
-
1
Pry::DEFAULT_PRINT
-
},
-
quiet: proc {
-
1
false
-
},
-
exception_handler: proc {
-
1
Pry::DEFAULT_EXCEPTION_HANDLER
-
},
-
exception_whitelist: proc {
-
1
Pry::DEFAULT_EXCEPTION_WHITELIST
-
},
-
hooks: proc {
-
1
Pry::DEFAULT_HOOKS
-
},
-
pager: proc {
-
1
true
-
},
-
system: proc {
-
Pry::DEFAULT_SYSTEM
-
},
-
color: proc {
-
1
Pry::Helpers::BaseHelpers.use_ansi_codes?
-
},
-
default_window_size: proc {
-
5
-
},
-
editor: proc {
-
Pry.default_editor_for_platform
-
}, # TODO: Pry::Platform.editor
-
should_load_rc: proc {
-
1
true
-
},
-
should_load_local_rc: proc {
-
1
true
-
},
-
should_trap_interrupts: proc {
-
1
Pry::Helpers::BaseHelpers.jruby?
-
}, # TODO: Pry::Platform.jruby?
-
disable_auto_reload: proc {
-
false
-
},
-
command_prefix: proc {
-
1
""
-
},
-
auto_indent: proc {
-
1
Pry::Helpers::BaseHelpers.use_ansi_codes?
-
},
-
correct_indent: proc {
-
1
true
-
},
-
collision_warning: proc {
-
1
false
-
},
-
output_prefix: proc {
-
1
"=> "
-
},
-
requires: proc {
-
1
[]
-
},
-
should_load_requires: proc {
-
1
true
-
},
-
should_load_plugins: proc {
-
1
true
-
},
-
windows_console_warning: proc {
-
true
-
},
-
control_d_handler: proc {
-
Pry::DEFAULT_CONTROL_D_HANDLER
-
},
-
memory_size: proc {
-
1
100
-
},
-
extra_sticky_locals: proc {
-
1
{}
-
},
-
command_completions: proc {
-
1
proc { commands.keys }
-
},
-
file_completions: proc {
-
proc { Dir["."] }
-
},
-
ls: proc {
-
Pry::Config.from_hash(Pry::Command::Ls::DEFAULT_OPTIONS)
-
},
-
completer: proc {
-
require "pry/input_completer"
-
Pry::InputCompleter
-
}
-
}
-
-
1
def initialize
-
1
super(nil)
-
1
configure_gist
-
1
configure_history
-
end
-
-
1
default.each do |key, value|
-
36
define_method(key) do
-
2286
if default[key].equal?(value)
-
27
default[key] = instance_eval(&value)
-
end
-
2286
default[key]
-
end
-
end
-
-
1
private
-
# TODO:
-
# all of this configure_* stuff is a relic of old code.
-
# we should try move this code to being command-local.
-
1
def configure_gist
-
1
self["gist"] = Pry::Config.from_hash(inspecter: proc(&:pretty_inspect))
-
end
-
-
1
def configure_history
-
1
self["history"] = Pry::Config.from_hash "should_save" => true,
-
"should_load" => true
-
1
history.file = File.expand_path("~/.pry_history") rescue nil
-
1
if history.file.nil?
-
self.should_load_rc = false
-
history.should_save = false
-
history.should_load = false
-
end
-
end
-
-
1
def lazy_readline
-
1
require 'readline'
-
1
Readline
-
rescue LoadError
-
warn "Sorry, you can't use Pry without Readline or a compatible library."
-
warn "Possible solutions:"
-
warn " * Rebuild Ruby with Readline support using `--with-readline`"
-
warn " * Use the rb-readline gem, which is a pure-Ruby port of Readline"
-
warn " * Use the pry-coolline gem, a pure-ruby alternative to Readline"
-
raise
-
end
-
end
-
1
class Pry
-
# @return [Array] Code of the method used when implementing Pry's
-
# __binding__, along with line indication to be used with instance_eval (and
-
# friends).
-
#
-
# @see Object#__binding__
-
1
BINDING_METHOD_IMPL = [<<-METHOD, __FILE__, __LINE__ + 1]
-
# Get a binding with 'self' set to self, and no locals.
-
#
-
# The default definee is determined by the context in which the
-
# definition is eval'd.
-
#
-
# Please don't call this method directly, see {__binding__}.
-
#
-
# @return [Binding]
-
def __pry__
-
binding
-
end
-
METHOD
-
end
-
-
1
class Object
-
# Start a Pry REPL on self.
-
#
-
# If `self` is a Binding then that will be used to evaluate expressions;
-
# otherwise a new binding will be created.
-
#
-
# @param [Object] object the object or binding to pry
-
# (__deprecated__, use `object.pry`)
-
# @param [Hash] hash the options hash
-
# @example With a binding
-
# binding.pry
-
# @example On any object
-
# "dummy".pry
-
# @example With options
-
# def my_method
-
# binding.pry :quiet => true
-
# end
-
# my_method()
-
# @see Pry.start
-
1
def pry(object=nil, hash={})
-
4
if object.nil? || Hash === object
-
4
Pry.start(self, object || {})
-
else
-
Pry.start(object, hash)
-
end
-
end
-
-
# Return a binding object for the receiver.
-
#
-
# The `self` of the binding is set to the current object, and it contains no
-
# local variables.
-
#
-
# The default definee (http://yugui.jp/articles/846) is set such that:
-
#
-
# * If `self` is a class or module, then new methods created in the binding
-
# will be defined in that class or module (as in `class Foo; end`).
-
# * If `self` is a normal object, then new methods created in the binding will
-
# be defined on its singleton class (as in `class << self; end`).
-
# * If `self` doesn't have a real singleton class (i.e. it is a Fixnum, Float,
-
# Symbol, nil, true, or false), then new methods will be created on the
-
# object's class (as in `self.class.class_eval{ }`)
-
#
-
# Newly created constants, including classes and modules, will also be added
-
# to the default definee.
-
#
-
# @return [Binding]
-
1
def __binding__
-
# If you ever feel like changing this method, be careful about variables
-
# that you use. They shouldn't be inserted into the binding that will
-
# eventually be returning.
-
-
# When you're cd'd into a class, methods you define should be added to it.
-
if is_a?(Module)
-
# class_eval sets both self and the default definee to this class.
-
return class_eval "binding"
-
end
-
-
unless respond_to?(:__pry__)
-
# The easiest way to check whether an object has a working singleton class
-
# is to try and define a method on it. (just checking for the presence of
-
# the singleton class gives false positives for `true` and `false`).
-
# __pry__ is just the closest method we have to hand, and using
-
# it has the nice property that we can memoize this check.
-
begin
-
# instance_eval sets the default definee to the object's singleton class
-
instance_eval(*Pry::BINDING_METHOD_IMPL)
-
-
# If we can't define methods on the Object's singleton_class. Then we fall
-
# back to setting the default definee to be the Object's class. That seems
-
# nicer than having a REPL in which you can't define methods.
-
rescue TypeError, Pry::FrozenObjectException
-
# class_eval sets the default definee to self.class
-
self.class.class_eval(*Pry::BINDING_METHOD_IMPL)
-
end
-
end
-
-
__pry__
-
end
-
end
-
-
1
class BasicObject
-
# Return a binding object for the receiver.
-
#
-
# The `self` of the binding is set to the current object, and it contains no
-
# local variables.
-
#
-
# The default definee (http://yugui.jp/articles/846) is set such that new
-
# methods defined will be added to the singleton class of the BasicObject.
-
#
-
# @return [Binding]
-
1
def __binding__
-
# BasicObjects don't have respond_to?, so we just define the method
-
# every time. As they also don't have `.freeze`, this call won't
-
# fail as it can for normal Objects.
-
(class << self; self; end).class_eval <<-EOF, __FILE__, __LINE__ + 1
-
# Get a binding with 'self' set to self, and no locals.
-
#
-
# The default definee is determined by the context in which the
-
# definition is eval'd.
-
#
-
# Please don't call this method directly, see {__binding__}.
-
#
-
# @return [Binding]
-
def __pry__
-
::Kernel.binding
-
end
-
EOF
-
self.__pry__
-
end
-
end
-
1
class Pry
-
1
class Editor
-
1
include Pry::Helpers::BaseHelpers
-
1
include Pry::Helpers::CommandHelpers
-
-
1
attr_reader :_pry_
-
-
1
def initialize(_pry_)
-
@_pry_ = _pry_
-
end
-
-
1
def edit_tempfile_with_content(initial_content, line=1)
-
temp_file do |f|
-
f.puts(initial_content)
-
f.flush
-
f.close(false)
-
invoke_editor(f.path, line, true)
-
File.read(f.path)
-
end
-
end
-
-
1
def invoke_editor(file, line, blocking=true)
-
raise CommandError, "Please set Pry.config.editor or export $VISUAL or $EDITOR" unless _pry_.config.editor
-
-
editor_invocation = build_editor_invocation_string(file, line, blocking)
-
return nil unless editor_invocation
-
-
if jruby?
-
open_editor_on_jruby(editor_invocation)
-
else
-
open_editor(editor_invocation)
-
end
-
end
-
-
1
private
-
-
# Generate the string that's used to start the editor. This includes
-
# all the flags we want as well as the file and line number we
-
# want to open at.
-
1
def build_editor_invocation_string(file, line, blocking)
-
-
if _pry_.config.editor.respond_to?(:call)
-
args = [file, line, blocking][0...(_pry_.config.editor.arity)]
-
_pry_.config.editor.call(*args)
-
else
-
sanitized_file = if windows?
-
file.gsub(/\//, '\\')
-
else
-
Shellwords.escape(file)
-
end
-
-
"#{_pry_.config.editor} #{blocking_flag_for_editor(blocking)} #{start_line_syntax_for_editor(sanitized_file, line)}"
-
end
-
end
-
-
# Start the editor running, using the calculated invocation string
-
1
def open_editor(editor_invocation)
-
# Note we dont want to use Pry.config.system here as that
-
# may be invoked non-interactively (i.e via Open4), whereas we want to
-
# ensure the editor is always interactive
-
system(*Shellwords.split(editor_invocation)) or raise CommandError, "`#{editor_invocation}` gave exit status: #{$?.exitstatus}"
-
end
-
-
# We need JRuby specific code here cos just shelling out using
-
# system() appears to be pretty broken :/
-
1
def open_editor_on_jruby(editor_invocation)
-
begin
-
require 'spoon'
-
pid = Spoon.spawnp(*Shellwords.split(editor_invocation))
-
Process.waitpid(pid)
-
rescue FFI::NotFoundError
-
system(editor_invocation)
-
end
-
end
-
-
# Some editors that run outside the terminal allow you to control whether or
-
# not to block the process from which they were launched (in this case, Pry).
-
# For those editors, return the flag that produces the desired behavior.
-
1
def blocking_flag_for_editor(blocking)
-
case editor_name
-
when /^emacsclient/
-
'--no-wait' unless blocking
-
when /^[gm]vim/
-
'--nofork' if blocking
-
when /^jedit/
-
'-wait' if blocking
-
when /^mate/, /^subl/, /^redcar/
-
'-w' if blocking
-
end
-
end
-
-
# Return the syntax for a given editor for starting the editor
-
# and moving to a particular line within that file
-
1
def start_line_syntax_for_editor(file_name, line_number)
-
# special case for 1st line
-
return file_name if line_number <= 1
-
-
case editor_name
-
when /^[gm]?vi/, /^emacs/, /^nano/, /^pico/, /^gedit/, /^kate/
-
"+#{line_number} #{file_name}"
-
when /^mate/, /^geany/
-
"-l #{line_number} #{file_name}"
-
when /^subl/
-
"#{file_name}:#{line_number}"
-
when /^uedit32/
-
"#{file_name}/#{line_number}"
-
when /^jedit/
-
"#{file_name} +line:#{line_number}"
-
when /^redcar/
-
"-l#{line_number} #{file_name}"
-
else
-
if windows?
-
"#{file_name}"
-
else
-
"+#{line_number} #{file_name}"
-
end
-
end
-
end
-
-
# Get the name of the binary that Pry.config.editor points to.
-
#
-
# This is useful for deciding which flags we pass to the editor as
-
# we can just use the program's name and ignore any absolute paths.
-
#
-
# @example
-
# Pry.config.editor="/home/conrad/bin/textmate -w"
-
# editor_name
-
# # => textmate
-
#
-
1
def editor_name
-
File.basename(_pry_.config.editor).split(" ").first
-
end
-
-
end
-
end
-
1
class Pry
-
-
# As a REPL, we often want to catch any unexpected exceptions that may have
-
# been raised; however we don't want to go overboard and prevent the user
-
# from exiting Pry when they want to.
-
1
module RescuableException
-
1
def self.===(exception)
-
6
case exception
-
# Catch when the user hits ^C (Interrupt < SignalException), and assume
-
# that they just wanted to stop the in-progress command (just like bash
-
# etc.)
-
when Interrupt
-
true
-
# Don't catch signals (particularly not SIGTERM) as these are unlikely
-
# to be intended for pry itself. We should also make sure that
-
# Kernel#exit works.
-
when *Pry.config.exception_whitelist
-
4
false
-
# All other exceptions will be caught.
-
else
-
2
true
-
end
-
end
-
end
-
-
# Catches SecurityErrors if $SAFE is set
-
1
module Pry::TooSafeException
-
1
def self.===(exception)
-
6
$SAFE > 0 && SecurityError === exception
-
end
-
end
-
-
# An Exception Tag (cf. Exceptional Ruby) that instructs Pry to show the error
-
# in a more user-friendly manner. This should be used when the exception
-
# happens within Pry itself as a direct consequence of the user typing
-
# something wrong.
-
#
-
# This allows us to distinguish between the user typing:
-
#
-
# pry(main)> def )
-
# SyntaxError: unexpected )
-
#
-
# pry(main)> method_that_evals("def )")
-
# SyntaxError: (eval):1: syntax error, unexpected ')'
-
# from ./a.rb:2 in `eval'
-
1
module UserError; end
-
-
# When we try to get a binding for an object, we try to define a method on
-
# that Object's singleton class. This doesn't work for "frozen" Object's, and
-
# the exception is just a vanilla RuntimeError.
-
1
module FrozenObjectException
-
1
def self.===(exception)
-
["can't modify frozen class/module",
-
"can't modify frozen Class",
-
].include?(exception.message)
-
end
-
end
-
-
# Don't catch these exceptions
-
1
DEFAULT_EXCEPTION_WHITELIST = [SystemExit,
-
SignalException,
-
Pry::TooSafeException]
-
-
# CommandErrors are caught by the REPL loop and displayed to the user. They
-
# indicate an exceptional condition that's fatal to the current command.
-
1
class CommandError < StandardError; end
-
1
class MethodNotFound < CommandError; end
-
-
# indicates obsolete API
-
1
class ObsoleteError < StandardError; end
-
-
# This is to keep from breaking under Rails 3.2 for people who are doing that
-
# IRB = Pry thing.
-
1
module ExtendCommandBundle
-
end
-
-
end
-
1
require "pry/helpers/base_helpers"
-
1
require "pry/helpers/options_helpers"
-
1
require "pry/helpers/command_helpers"
-
1
require "pry/helpers/text"
-
1
require "pry/helpers/table"
-
1
class Pry
-
1
module Helpers
-
-
1
module BaseHelpers
-
-
1
module_function
-
-
1
def silence_warnings
-
old_verbose = $VERBOSE
-
$VERBOSE = nil
-
begin
-
yield
-
ensure
-
$VERBOSE = old_verbose
-
end
-
end
-
-
# Acts like send but ignores any methods defined below Object or Class in the
-
# inheritance hierarchy.
-
# This is required to introspect methods on objects like Net::HTTP::Get that
-
# have overridden the `method` method.
-
1
def safe_send(obj, method, *args, &block)
-
8
(Module === obj ? Module : Object).instance_method(method).bind(obj).call(*args, &block)
-
end
-
1
public :safe_send
-
-
1
def find_command(name, set = Pry::Commands)
-
command_match = set.find do |_, command|
-
(listing = command.options[:listing]) == name && listing != nil
-
end
-
command_match.last if command_match
-
end
-
-
1
def not_a_real_file?(file)
-
file =~ /(\(.*\))|<.*>/ || file =~ /__unknown__/ || file == "" || file == "-e"
-
end
-
-
1
def command_dependencies_met?(options)
-
4
return true if !options[:requires_gem]
-
4
Array(options[:requires_gem]).all? do |g|
-
Rubygem.installed?(g)
-
end
-
end
-
-
1
def use_ansi_codes?
-
2
windows_ansi? || ENV['TERM'] && ENV['TERM'] != "dumb"
-
end
-
-
1
def colorize_code(code)
-
39
CodeRay.scan(code, :ruby).term
-
end
-
-
1
def highlight(string, regexp, highlight_color=:bright_yellow)
-
string.gsub(regexp) { |match| "<#{highlight_color}>#{match}</#{highlight_color}>" }
-
end
-
-
# formatting
-
1
def heading(text)
-
text = "#{text}\n--"
-
"\e[1m#{text}\e[0m"
-
end
-
-
# have fun on the Windows platform.
-
1
def windows?
-
6
RbConfig::CONFIG['host_os'] =~ /mswin|mingw/
-
end
-
-
# are we able to use ansi on windows?
-
1
def windows_ansi?
-
5
defined?(Win32::Console) || ENV['ANSICON'] || (windows? && mri_2?)
-
end
-
-
1
def jruby?
-
29
RbConfig::CONFIG['ruby_install_name'] == 'jruby'
-
end
-
-
1
def jruby_19?
-
jruby? && RbConfig::CONFIG['ruby_version'] == '1.9'
-
end
-
-
1
def rbx?
-
3
RbConfig::CONFIG['ruby_install_name'] == 'rbx'
-
end
-
-
1
def mri?
-
RbConfig::CONFIG['ruby_install_name'] == 'ruby'
-
end
-
-
1
def mri_19?
-
mri? && RUBY_VERSION =~ /^1\.9/
-
end
-
-
1
def mri_2?
-
mri? && RUBY_VERSION =~ /^2/
-
end
-
-
1
def mri_20?
-
mri? && RUBY_VERSION =~ /^2\.0/
-
end
-
-
1
def mri_21?
-
mri? && RUBY_VERSION =~ /^2\.1/
-
end
-
-
# Send the given text through the best available pager (if Pry.config.pager is
-
# enabled). Infers where to send the output if used as a mixin.
-
# DEPRECATED.
-
1
def stagger_output(text, out = nil)
-
Pry.new.pager.page text
-
end
-
end
-
end
-
end
-
1
class Pry
-
1
module Helpers
-
-
1
module CommandHelpers
-
1
include OptionsHelpers
-
-
1
module_function
-
-
# Open a temp file and yield it to the block, closing it after
-
# @return [String] The path of the temp file
-
1
def temp_file(ext='.rb')
-
file = Tempfile.new(['pry', ext])
-
yield file
-
ensure
-
file.close(true) if file
-
end
-
-
1
def internal_binding?(target)
-
8
m = target.eval("::Kernel.__method__").to_s
-
# class_eval is here because of http://jira.codehaus.org/browse/JRUBY-6753
-
8
["__binding__", "__pry__", "class_eval"].include?(m)
-
end
-
-
1
def get_method_or_raise(name, target, opts={}, omit_help=false)
-
meth = Pry::Method.from_str(name, target, opts)
-
-
if name && !meth
-
command_error("The method '#{name}' could not be found.", omit_help, MethodNotFound)
-
end
-
-
(opts[:super] || 0).times do
-
if meth.super
-
meth = meth.super
-
else
-
command_error("'#{meth.name_with_owner}' has no super method.", omit_help, MethodNotFound)
-
end
-
end
-
-
if !meth || (!name && internal_binding?(target))
-
command_error("No method name given, and context is not a method.", omit_help, MethodNotFound)
-
end
-
-
set_file_and_dir_locals(meth.source_file)
-
meth
-
end
-
-
1
def command_error(message, omit_help, klass=CommandError)
-
message += " Type `#{command_name} --help` for help." unless omit_help
-
raise klass, message
-
end
-
-
# Remove any common leading whitespace from every line in `text`.
-
#
-
# This can be used to make a HEREDOC line up with the left margin, without
-
# sacrificing the indentation level of the source code.
-
#
-
# e.g.
-
# opt.banner unindent <<-USAGE
-
# Lorem ipsum dolor sit amet, consectetur adipisicing elit,
-
# sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
-
# "Ut enim ad minim veniam."
-
# USAGE
-
#
-
# Heavily based on textwrap.dedent from Python, which is:
-
# Copyright (C) 1999-2001 Gregory P. Ward.
-
# Copyright (C) 2002, 2003 Python Software Foundation.
-
# Written by Greg Ward <gward@python.net>
-
#
-
# Licensed under <http://docs.python.org/license.html>
-
# From <http://hg.python.org/cpython/file/6b9f0a6efaeb/Lib/textwrap.py>
-
#
-
# @param [String] text The text from which to remove indentation
-
# @return [String] The text with indentation stripped.
-
1
def unindent(text, left_padding = 0)
-
# Empty blank lines
-
8
text = text.sub(/^[ \t]+$/, '')
-
-
# Find the longest common whitespace to all indented lines
-
# Ignore lines containing just -- or ++ as these seem to be used by
-
# comment authors as delimeters.
-
8
margin = text.scan(/^[ \t]*(?!--\n|\+\+\n)(?=[^ \t\n])/).inject do |current_margin, next_indent|
-
72
if next_indent.start_with?(current_margin)
-
72
current_margin
-
elsif current_margin.start_with?(next_indent)
-
next_indent
-
else
-
""
-
end
-
end
-
-
8
text.gsub(/^#{margin}/, ' ' * left_padding)
-
end
-
-
# Restrict a string to the given range of lines (1-indexed)
-
# @param [String] content The string.
-
# @param [Range, Fixnum] lines The line(s) to restrict it to.
-
# @return [String] The resulting string.
-
1
def restrict_to_lines(content, lines)
-
line_range = one_index_range_or_number(lines)
-
Array(content.lines.to_a[line_range]).join
-
end
-
-
1
def one_index_number(line_number)
-
if line_number > 0
-
line_number - 1
-
else
-
line_number
-
end
-
end
-
-
# convert a 1-index range to a 0-indexed one
-
1
def one_index_range(range)
-
Range.new(one_index_number(range.begin), one_index_number(range.end))
-
end
-
-
1
def one_index_range_or_number(range_or_number)
-
case range_or_number
-
when Range
-
one_index_range(range_or_number)
-
else
-
one_index_number(range_or_number)
-
end
-
end
-
-
1
def absolute_index_number(line_number, array_length)
-
if line_number >= 0
-
line_number
-
else
-
[array_length + line_number, 0].max
-
end
-
end
-
-
1
def absolute_index_range(range_or_number, array_length)
-
case range_or_number
-
when Range
-
a = absolute_index_number(range_or_number.begin, array_length)
-
b = absolute_index_number(range_or_number.end, array_length)
-
else
-
a = b = absolute_index_number(range_or_number, array_length)
-
end
-
-
Range.new(a, b)
-
end
-
-
1
def set_file_and_dir_locals(file_name, _pry_=_pry_(), target=target())
-
4
return if !target or !file_name
-
4
_pry_.last_file = File.expand_path(file_name)
-
4
_pry_.inject_local("_file_", _pry_.last_file, target)
-
-
4
_pry_.last_dir = File.dirname(_pry_.last_file)
-
4
_pry_.inject_local("_dir_", _pry_.last_dir, target)
-
end
-
end
-
-
end
-
end
-
1
class Pry
-
1
module Helpers
-
-
# This class contains methods useful for extracting
-
# documentation from methods and classes.
-
1
module DocumentationHelpers
-
-
1
module_function
-
-
1
def process_rdoc(comment)
-
comment = comment.dup
-
comment.gsub(/<code>(?:\s*\n)?(.*?)\s*<\/code>/m) { CodeRay.scan($1, :ruby).term }.
-
gsub(/<em>(?:\s*\n)?(.*?)\s*<\/em>/m) { "\e[1m#{$1}\e[0m" }.
-
gsub(/<i>(?:\s*\n)?(.*?)\s*<\/i>/m) { "\e[1m#{$1}\e[0m" }.
-
gsub(/\B\+(\w+?)\+\B/) { "\e[32m#{$1}\e[0m" }.
-
gsub(/((?:^[ \t]+.+(?:\n+|\Z))+)/) { CodeRay.scan($1, :ruby).term }.
-
gsub(/`(?:\s*\n)?([^\e]*?)\s*`/) { "`#{CodeRay.scan($1, :ruby).term}`" }
-
end
-
-
1
def process_yardoc_tag(comment, tag)
-
in_tag_block = nil
-
comment.lines.map do |v|
-
if in_tag_block && v !~ /^\S/
-
Pry::Helpers::Text.strip_color Pry::Helpers::Text.strip_color(v)
-
elsif in_tag_block
-
in_tag_block = false
-
v
-
else
-
in_tag_block = true if v =~ /^@#{tag}/
-
v
-
end
-
end.join
-
end
-
-
1
def process_yardoc(comment)
-
yard_tags = ["param", "return", "option", "yield", "attr", "attr_reader", "attr_writer",
-
"deprecate", "example", "raise"]
-
(yard_tags - ["example"]).inject(comment) { |a, v| process_yardoc_tag(a, v) }.
-
gsub(/^@(#{yard_tags.join("|")})/) { "\e[33m#{$1}\e[0m" }
-
end
-
-
1
def process_comment_markup(comment)
-
process_yardoc process_rdoc(comment)
-
end
-
-
# @param [String] code
-
# @return [String]
-
1
def strip_comments_from_c_code(code)
-
code.sub(/\A\s*\/\*.*?\*\/\s*/m, '')
-
end
-
-
# Given a string that makes up a comment in a source-code file parse out the content
-
# that the user is intended to read. (i.e. without leading indentation, #-characters
-
# or shebangs)
-
#
-
# @param [String] comment
-
# @return [String]
-
1
def get_comment_content(comment)
-
comment = comment.dup
-
# Remove #!/usr/bin/ruby
-
comment.gsub!(/\A\#!.*$/, '')
-
# Remove leading empty comment lines
-
comment.gsub!(/\A\#+?$/, '')
-
comment.gsub!(/^\s*#/, '')
-
strip_leading_whitespace(comment)
-
end
-
-
# @param [String] text
-
# @return [String]
-
1
def strip_leading_whitespace(text)
-
4
Pry::Helpers::CommandHelpers.unindent(text)
-
end
-
end
-
end
-
end
-
1
class Pry
-
1
module Helpers
-
1
module OptionsHelpers
-
1
module_function
-
-
# Add method options to the Slop instance
-
1
def method_options(opt)
-
@method_target = target
-
opt.on :M, "instance-methods", "Operate on instance methods."
-
opt.on :m, :methods, "Operate on methods."
-
opt.on :s, :super, "Select the 'super' method. Can be repeated to traverse the ancestors.", :as => :count
-
opt.on :c, :context, "Select object context to run under.", :argument => true do |context|
-
@method_target = Pry.binding_for(target.eval(context))
-
end
-
end
-
-
# Get the method object parsed by the slop instance
-
1
def method_object
-
@method_object ||= get_method_or_raise(args.empty? ? nil : args.join(" "), @method_target,
-
:super => opts[:super],
-
:instance => opts.present?(:'instance-methods') && !opts.present?(:'methods'),
-
:methods => opts.present?(:'methods') && !opts.present?(:'instance-methods')
-
)
-
end
-
end
-
end
-
end
-
1
class Pry
-
1
module Helpers
-
1
def self.tablify_or_one_line(heading, things)
-
plain_heading = Pry::Helpers::Text.strip_color(heading)
-
attempt = Table.new(things, :column_count => things.size)
-
if attempt.fits_on_line?(Terminal.width! - plain_heading.size - 2)
-
"#{heading}: #{attempt}\n"
-
else
-
"#{heading}: \n#{tablify_to_screen_width(things, :indent => ' ')}\n"
-
end
-
end
-
-
1
def self.tablify_to_screen_width(things, options = {})
-
things = things.compact
-
if indent = options[:indent]
-
usable_width = Terminal.width! - indent.size
-
tablify(things, usable_width).to_s.gsub(/^/, indent)
-
else
-
tablify(things, Terminal.width!).to_s
-
end
-
end
-
-
1
def self.tablify(things, line_length)
-
table = Table.new(things, :column_count => things.size)
-
table.column_count -= 1 until 1 == table.column_count or
-
table.fits_on_line?(line_length)
-
table
-
end
-
-
1
class Table
-
1
attr_reader :items, :column_count
-
1
def initialize items, args = {}
-
@column_count = args[:column_count]
-
self.items = items
-
end
-
-
1
def to_s
-
rows_to_s.join("\n")
-
end
-
-
1
def rows_to_s style = :color_on
-
widths = columns.map{|e| _max_width(e)}
-
@rows_without_colors.map do |r|
-
padded = []
-
r.each_with_index do |e,i|
-
next unless e
-
item = e.ljust(widths[i])
-
item.sub! e, _recall_color_for(e) if :color_on == style
-
padded << item
-
end
-
padded.join(Pry.config.ls.separator)
-
end
-
end
-
-
1
def items= items
-
@items = items
-
_rebuild_colorless_cache
-
_recolumn
-
items
-
end
-
-
1
def column_count= n
-
@column_count = n
-
_recolumn
-
end
-
-
1
def fits_on_line? line_length
-
_max_width(rows_to_s :no_color) <= line_length
-
end
-
-
1
def columns
-
@rows_without_colors.transpose
-
end
-
-
1
def ==(other); items == other.to_a end
-
1
def to_a; items.to_a end
-
-
1
private
-
1
def _max_width(things)
-
things.compact.map(&:size).max || 0
-
end
-
-
1
def _rebuild_colorless_cache
-
@colorless_cache = {}
-
@plain_items = []
-
items.map do |e|
-
plain = Pry::Helpers::Text.strip_color(e)
-
@colorless_cache[plain] = e
-
@plain_items << plain
-
end
-
end
-
-
1
def _recolumn
-
@rows_without_colors = []
-
return if items.size.zero?
-
row_count = (items.size.to_f/column_count).ceil
-
row_count.times do |i|
-
row_indices = (0...column_count).map{|e| row_count*e+i}
-
@rows_without_colors << row_indices.map{|e| @plain_items[e]}
-
end
-
end
-
-
1
def _recall_color_for thing
-
@colorless_cache[thing]
-
end
-
end
-
-
end
-
end
-
1
class Pry
-
1
module Helpers
-
-
# The methods defined on {Text} are available to custom commands via {Pry::Command#text}.
-
1
module Text
-
-
1
COLORS =
-
{
-
"black" => 0,
-
"red" => 1,
-
"green" => 2,
-
"yellow" => 3,
-
"blue" => 4,
-
"purple" => 5,
-
"magenta" => 5,
-
"cyan" => 6,
-
"white" => 7
-
}
-
-
1
class << self
-
-
1
COLORS.each_pair do |color, value|
-
9
define_method color do |text|
-
"\033[0;#{30+value}m#{text}\033[0m"
-
end
-
-
9
define_method "bright_#{color}" do |text|
-
"\033[1;#{30+value}m#{text}\033[0m"
-
end
-
end
-
-
# Remove any color codes from _text_.
-
#
-
# @param [String, #to_s] text
-
# @return [String] _text_ stripped of any color codes.
-
1
def strip_color(text)
-
text.to_s.gsub(/\e\[.*?(\d)+m/ , '')
-
end
-
-
# Returns _text_ as bold text for use on a terminal.
-
#
-
# @param [String, #to_s] text
-
# @return [String] _text_
-
1
def bold(text)
-
4
"\e[1m#{text}\e[0m"
-
end
-
-
# Returns `text` in the default foreground colour.
-
# Use this instead of "black" or "white" when you mean absence of colour.
-
#
-
# @param [String, #to_s] text
-
# @return [String]
-
1
def default(text)
-
text.to_s
-
end
-
1
alias_method :bright_default, :bold
-
-
# Executes the block with `Pry.config.color` set to false.
-
# @yield
-
# @return [void]
-
1
def no_color(&block)
-
boolean = Pry.config.color
-
Pry.config.color = false
-
yield
-
ensure
-
Pry.config.color = boolean
-
end
-
-
# Executes the block with `Pry.config.pager` set to false.
-
# @yield
-
# @return [void]
-
1
def no_pager(&block)
-
boolean = Pry.config.pager
-
Pry.config.pager = false
-
yield
-
ensure
-
Pry.config.pager = boolean
-
end
-
-
# Returns _text_ in a numbered list, beginning at _offset_.
-
#
-
# @param [#each_line] text
-
# @param [Fixnum] offset
-
# @return [String]
-
1
def with_line_numbers(text, offset, color=:blue)
-
lines = text.each_line.to_a
-
max_width = (offset + lines.count).to_s.length
-
lines.each_with_index.map do |line, index|
-
adjusted_index = (index + offset).to_s.rjust(max_width)
-
"#{self.send(color, adjusted_index)}: #{line}"
-
end.join
-
end
-
-
# Returns _text_ indented by _chars_ spaces.
-
#
-
# @param [String] text
-
# @param [Fixnum] chars
-
1
def indent(text, chars)
-
text.lines.map { |l| "#{' ' * chars}#{l}" }.join
-
end
-
end
-
-
end
-
-
end
-
end
-
-
1
class Pry
-
# The History class is responsible for maintaining the user's input history,
-
# both internally and within Readline.
-
1
class History
-
1
attr_accessor :loader, :saver, :pusher, :clearer
-
-
# @return [Fixnum] Number of lines in history when Pry first loaded.
-
1
attr_reader :original_lines
-
-
1
def initialize(options={})
-
1
@history = []
-
1
@original_lines = 0
-
1
@file_path = options[:file_path]
-
1
restore_default_behavior
-
end
-
-
# Assign the default methods for loading, saving, pushing, and clearing.
-
1
def restore_default_behavior
-
1
Pry.config.input # force Readline to load if applicable
-
-
1
@loader = method(:read_from_file)
-
1
@saver = method(:save_to_file)
-
-
1
if defined?(Readline)
-
1
@pusher = method(:push_to_readline)
-
1
@clearer = method(:clear_readline)
-
else
-
@pusher = proc { }
-
@clearer = proc { }
-
end
-
end
-
-
# Load the input history using `History.loader`.
-
# @return [Integer] The number of lines loaded
-
1
def load
-
1
@loader.call do |line|
-
31
@pusher.call(line.chomp)
-
31
@history << line.chomp
-
31
@original_lines += 1
-
end
-
end
-
-
# Add a line to the input history, ignoring blank and duplicate lines.
-
# @param [String] line
-
# @return [String] The same line that was passed in
-
1
def push(line)
-
24
unless line.empty? || (@history.last && line == @history.last)
-
12
@pusher.call(line)
-
12
@history << line
-
12
@saver.call(line) if Pry.config.history.should_save
-
end
-
24
line
-
end
-
1
alias << push
-
-
# Clear this session's history. This won't affect the contents of the
-
# history file.
-
1
def clear
-
@clearer.call
-
@history = []
-
end
-
-
# @return [Fixnum] The number of lines in history.
-
1
def history_line_count
-
24
@history.count
-
end
-
-
# @return [Fixnum] The number of lines in history from just this session.
-
1
def session_line_count
-
24
@history.count - @original_lines
-
end
-
-
# Return an Array containing all stored history.
-
# @return [Array<String>] An Array containing all lines of history loaded
-
# or entered by the user in the current session.
-
1
def to_a
-
@history.dup
-
end
-
-
1
private
-
-
# The default loader. Yields lines from `Pry.history.config.file`.
-
1
def read_from_file
-
1
path = history_file_path
-
-
1
if File.exists?(path)
-
32
File.foreach(path) { |line| yield(line) }
-
end
-
rescue => error
-
warn "History file not loaded: #{error.message}"
-
end
-
-
# The default pusher. Appends the given line to Readline::HISTORY.
-
# @param [String] line
-
1
def push_to_readline(line)
-
43
Readline::HISTORY << line
-
end
-
-
# The default clearer. Clears Readline::HISTORY.
-
1
def clear_readline
-
Readline::HISTORY.shift until Readline::HISTORY.empty?
-
end
-
-
# The default saver. Appends the given line to `Pry.history.config.file`.
-
1
def save_to_file(line)
-
12
history_file.puts line if history_file
-
end
-
-
# The history file, opened for appending.
-
1
def history_file
-
24
if defined?(@history_file)
-
23
@history_file
-
else
-
1
@history_file = File.open(history_file_path, 'a', 0600).tap do |file|
-
1
file.sync = true
-
end
-
end
-
rescue Errno::EACCES
-
warn 'History not saved; unable to open your history file for writing.'
-
@history_file = false
-
end
-
-
1
def history_file_path
-
2
File.expand_path(@file_path || Pry.config.history.file)
-
end
-
end
-
end
-
1
class Pry
-
# A history array is an array to which you can only add elements. Older
-
# entries are removed progressively, so that the array never contains more than
-
# N elements.
-
#
-
# History arrays are used by Pry to store the output of the last commands.
-
#
-
# @example
-
# ary = Pry::HistoryArray.new 10
-
# ary << 1 << 2 << 3
-
# ary[0] # => 1
-
# ary[1] # => 2
-
# 10.times { |n| ary << n }
-
# ary[0] # => nil
-
# ary[-1] # => 9
-
1
class HistoryArray
-
1
include Enumerable
-
-
# @param [Integer] size Maximum amount of objects in the array
-
1
def initialize(size)
-
8
@max_size = size
-
-
8
@hash = {}
-
8
@count = 0
-
end
-
-
# Pushes an object at the end of the array
-
# @param [Object] value Object to be added
-
1
def <<(value)
-
25
@hash[@count] = value
-
-
25
if @hash.size > max_size
-
@hash.delete(@count - max_size)
-
end
-
-
25
@count += 1
-
-
25
self
-
end
-
-
# @overload [](index)
-
# @param [Integer] index Index of the item to access.
-
# @return [Object, nil] Item at that index or nil if it has been removed.
-
# @overload [](index, size)
-
# @param [Integer] index Index of the first item to access.
-
# @param [Integer] size Amount of items to access
-
# @return [Array, nil] The selected items. Nil if index is greater than
-
# the size of the array.
-
# @overload [](range)
-
# @param [Range<Integer>] range Range of indices to access.
-
# @return [Array, nil] The selected items. Nil if index is greater than
-
# the size of the array.
-
1
def [](index_or_range, size = nil)
-
34
if index_or_range.is_a? Integer
-
34
index = convert_index(index_or_range)
-
-
34
if size
-
end_index = index + size
-
index > @count ? nil : (index...[end_index, @count].min).map do |n|
-
@hash[n]
-
end
-
else
-
34
@hash[index]
-
end
-
else
-
range = convert_range(index_or_range)
-
range.begin > @count ? nil : range.map { |n| @hash[n] }
-
end
-
end
-
-
# @return [Integer] Amount of objects in the array
-
1
def size
-
48
@count
-
end
-
1
alias count size
-
1
alias length size
-
-
1
def empty?
-
size == 0
-
end
-
-
1
def each
-
((@count - size)...@count).each do |n|
-
yield @hash[n]
-
end
-
end
-
-
1
def to_a
-
((@count - size)...@count).map { |n| @hash[n] }
-
end
-
-
# Returns [Hash] copy of the internal @hash history
-
1
def to_h
-
@hash.dup
-
end
-
-
1
def pop!
-
@hash.delete @count - 1
-
@count -= 1
-
end
-
-
1
def inspect
-
"#<#{self.class} size=#{size} first=#{@count - size} max_size=#{max_size}>"
-
end
-
-
# @return [Integer] Maximum amount of objects in the array
-
1
attr_reader :max_size
-
-
1
private
-
1
def convert_index(n)
-
34
n >= 0 ? n : @count + n
-
end
-
-
1
def convert_range(range)
-
end_index = convert_index(range.end)
-
end_index += 1 unless range.exclude_end?
-
-
Range.new(convert_index(range.begin), [end_index, @count].min, true)
-
end
-
end
-
end
-
1
class Pry
-
-
# Implements a hooks system for Pry. A hook is a callable that is
-
# associated with an event. A number of events are currently
-
# provided by Pry, these include: `:when_started`, `:before_session`, `:after_session`.
-
# A hook must have a name, and is connected with an event by the
-
# `Pry::Hooks#add_hook` method.
-
# @example Adding a hook for the `:before_session` event.
-
# Pry.config.hooks.add_hook(:before_session, :say_hi) do
-
# puts "hello"
-
# end
-
1
class Hooks
-
-
# Converts a hash to a `Pry::Hooks` instance. All hooks defined
-
# this way are anonymous. This functionality is primarily to
-
# provide backwards-compatibility with the old hash-based hook
-
# system in Pry versions < 0.9.8
-
# @param [Hash] hash The hash to convert to `Pry::Hooks`.
-
# @return [Pry::Hooks] The resulting `Pry::Hooks` instance.
-
1
def self.from_hash(hash)
-
return hash if hash.instance_of?(self)
-
instance = new
-
hash.each do |k, v|
-
instance.add_hook(k, nil, v)
-
end
-
instance
-
end
-
-
1
def initialize
-
1
@hooks = {}
-
end
-
-
# Ensure that duplicates have their @hooks object
-
1
def initialize_copy(orig)
-
5
hooks_dup = @hooks.dup
-
5
@hooks.each do |k, v|
-
5
hooks_dup[k] = v.dup
-
end
-
-
5
@hooks = hooks_dup
-
end
-
-
1
def hooks
-
@hooks
-
end
-
1
protected :hooks
-
-
1
def errors
-
111
@errors ||= []
-
end
-
-
# Destructively merge the contents of two `Pry:Hooks` instances.
-
# @param [Pry::Hooks] other The `Pry::Hooks` instance to merge
-
# @return [Pry:Hooks] Returns the receiver.
-
# @example
-
# hooks = Pry::Hooks.new.add_hook(:before_session, :say_hi) { puts "hi!" }
-
# Pry::Hooks.new.merge!(hooks)
-
1
def merge!(other)
-
@hooks.merge!(other.dup.hooks) do |key, v1, v2|
-
merge_arrays(v1, v2)
-
end
-
-
self
-
end
-
-
1
def merge_arrays(array1, array2)
-
uniq_keeping_last(array1 + array2, &:first)
-
end
-
1
private :merge_arrays
-
-
1
def uniq_keeping_last(input, &block)
-
hash, output = {}, []
-
input.reverse.each{ |i| hash[block[i]] ||= (output.unshift i) }
-
output
-
end
-
1
private :uniq_keeping_last
-
-
# Return a new `Pry::Hooks` instance containing a merge of the contents of two `Pry:Hooks` instances,
-
# @param [Pry::Hooks] other The `Pry::Hooks` instance to merge
-
# @return [Pry::Hooks] The new hash.
-
# @example
-
# hooks = Pry::Hooks.new.add_hook(:before_session, :say_hi) { puts "hi!" }
-
# Pry::Hooks.new.merge(hooks)
-
1
def merge(other)
-
self.dup.tap do |v|
-
v.merge!(other)
-
end
-
end
-
-
# Add a new hook to be executed for the `name` even.
-
# @param [Symbol] event_name The name of the event.
-
# @param [Symbol] hook_name The name of the hook.
-
# @param [#call] callable The callable.
-
# @yield The block to use as the callable (if `callable` parameter not provided)
-
# @return [Pry:Hooks] Returns the receiver.
-
# @example
-
# Pry::Hooks.new.add_hook(:before_session, :say_hi) { puts "hi!" }
-
1
def add_hook(event_name, hook_name, callable=nil, &block)
-
1
@hooks[event_name] ||= []
-
-
# do not allow duplicates, but allow multiple `nil` hooks
-
# (anonymous hooks)
-
1
if hook_exists?(event_name, hook_name) && !hook_name.nil?
-
raise ArgumentError, "Hook with name '#{hook_name}' already defined!"
-
end
-
-
1
if !block && !callable
-
raise ArgumentError, "Must provide a block or callable."
-
end
-
-
# ensure we only have one anonymous hook
-
1
@hooks[event_name].delete_if { |h, k| h.nil? } if hook_name.nil?
-
-
1
if block
-
1
@hooks[event_name] << [hook_name, block]
-
elsif callable
-
@hooks[event_name] << [hook_name, callable]
-
end
-
-
1
self
-
end
-
-
# Execute the list of hooks for the `event_name` event.
-
# @param [Symbol] event_name The name of the event.
-
# @param [Array] args The arguments to pass to each hook function.
-
# @return [Object] The return value of the last executed hook.
-
# @example
-
# my_hooks = Pry::Hooks.new.add_hook(:before_session, :say_hi) { puts "hi!" }
-
# my_hooks.exec_hook(:before_session) #=> OUTPUT: "hi!"
-
1
def exec_hook(event_name, *args, &block)
-
56
@hooks[event_name] ||= []
-
-
@hooks[event_name].map do |hook_name, callable|
-
4
begin
-
4
callable.call(*args, &block)
-
rescue RescuableException => e
-
errors << e
-
e
-
end
-
56
end.last
-
end
-
-
# Return the number of hook functions registered for the `event_name` event.
-
# @param [Symbol] event_name The name of the event.
-
# @return [Fixnum] The number of hook functions for `event_name`.
-
# @example
-
# my_hooks = Pry::Hooks.new.add_hook(:before_session, :say_hi) { puts "hi!" }
-
# my_hooks.count(:before_session) #=> 1
-
1
def hook_count(event_name)
-
@hooks[event_name] ||= []
-
@hooks[event_name].size
-
end
-
-
# Return a specific hook for a given event.
-
# @param [Symbol] event_name The name of the event.
-
# @param [Symbol] hook_name The name of the hook
-
# @return [#call] The requested hook.
-
# @example
-
# my_hooks = Pry::Hooks.new.add_hook(:before_session, :say_hi) { puts "hi!" }
-
# my_hooks.get_hook(:before_session, :say_hi).call #=> "hi!"
-
1
def get_hook(event_name, hook_name)
-
@hooks[event_name] ||= []
-
hook = @hooks[event_name].find { |current_hook_name, callable| current_hook_name == hook_name }
-
hook.last if hook
-
end
-
-
# Return the hash of hook names / hook functions for a
-
# given event. (Note that modifying the returned hash does not
-
# alter the hooks, use add_hook/delete_hook for that).
-
# @param [Symbol] event_name The name of the event.
-
# @return [Hash] The hash of hook names / hook functions.
-
# @example
-
# my_hooks = Pry::Hooks.new.add_hook(:before_session, :say_hi) { puts "hi!" }
-
# my_hooks.get_hooks(:before_session) #=> {:say_hi=>#<Proc:0x00000101645e18@(pry):9>}
-
1
def get_hooks(event_name)
-
@hooks[event_name] ||= []
-
Hash[@hooks[event_name]]
-
end
-
-
# Delete a hook for an event.
-
# @param [Symbol] event_name The name of the event.
-
# @param [Symbol] hook_name The name of the hook.
-
# to delete.
-
# @return [#call] The deleted hook.
-
# @example
-
# my_hooks = Pry::Hooks.new.add_hook(:before_session, :say_hi) { puts "hi!" }
-
# my_hooks.delete_hook(:before_session, :say_hi)
-
1
def delete_hook(event_name, hook_name)
-
@hooks[event_name] ||= []
-
deleted_callable = nil
-
-
@hooks[event_name].delete_if do |current_hook_name, callable|
-
if current_hook_name == hook_name
-
deleted_callable = callable
-
true
-
else
-
false
-
end
-
end
-
deleted_callable
-
end
-
-
# Clear all hooks functions for a given event.
-
# @param [String] event_name The name of the event.
-
# @example
-
# my_hooks = Pry::Hooks.new.add_hook(:before_session, :say_hi) { puts "hi!" }
-
# my_hooks.delete_hook(:before_session)
-
1
def delete_hooks(event_name)
-
@hooks[event_name] = []
-
end
-
-
1
alias_method :clear, :delete_hooks
-
-
# Remove all events and hooks, clearing out the Pry::Hooks
-
# instance completely.
-
# @example
-
# my_hooks = Pry::Hooks.new.add_hook(:before_session, :say_hi) { puts "hi!" }
-
# my_hooks.clear_all
-
1
def clear_all
-
@hooks = {}
-
end
-
-
# @param [Symbol] event_name Name of the event.
-
# @param [Symbol] hook_name Name of the hook.
-
# @return [Boolean] Whether the hook by the name `hook_name`
-
1
def hook_exists?(event_name, hook_name)
-
1
!!(@hooks[event_name] && @hooks[event_name].find { |name, _| name == hook_name })
-
end
-
end
-
end
-
1
require 'coderay'
-
-
1
class Pry
-
##
-
# Pry::Indent is a class that can be used to indent a number of lines
-
# containing Ruby code similar as to how IRB does it (but better). The class
-
# works by tokenizing a string using CodeRay and then looping over those
-
# tokens. Based on the tokens in a line of code that line (or the next one)
-
# will be indented or un-indented by correctly.
-
#
-
1
class Indent
-
1
include Helpers::BaseHelpers
-
-
# Raised if {#module_nesting} would not work.
-
1
class UnparseableNestingError < StandardError; end
-
-
# @return [String] String containing the spaces to be inserted before the next line.
-
1
attr_reader :indent_level
-
-
# @return [Array<String>] The stack of open tokens.
-
1
attr_reader :stack
-
-
# The amount of spaces to insert for each indent level.
-
1
SPACES = ' '
-
-
# Hash containing all the tokens that should increase the indentation
-
# level. The keys of this hash are open tokens, the values the matching
-
# tokens that should prevent a line from being indented if they appear on
-
# the same line.
-
1
OPEN_TOKENS = {
-
'def' => 'end',
-
'class' => 'end',
-
'module' => 'end',
-
'do' => 'end',
-
'if' => 'end',
-
'unless' => 'end',
-
'while' => 'end',
-
'until' => 'end',
-
'for' => 'end',
-
'case' => 'end',
-
'begin' => 'end',
-
'[' => ']',
-
'{' => '}',
-
'(' => ')'
-
}
-
-
# Which tokens can either be open tokens, or appear as modifiers on
-
# a single-line.
-
1
SINGLELINE_TOKENS = %w(if while until unless rescue)
-
-
# Which tokens can be followed by an optional "do" keyword.
-
1
OPTIONAL_DO_TOKENS = %w(for while until)
-
-
# Collection of token types that should be ignored. Without this list
-
# keywords such as "class" inside strings would cause the code to be
-
# indented incorrectly.
-
#
-
# :pre_constant and :preserved_constant are the CodeRay 0.9.8 and 1.0.0
-
# classifications of "true", "false", and "nil".
-
1
IGNORE_TOKENS = [:space, :content, :string, :method, :ident,
-
:constant, :pre_constant, :predefined_constant]
-
-
# Tokens that indicate the end of a statement (i.e. that, if they appear
-
# directly before an "if" indicates that that if applies to the same line,
-
# not the next line)
-
#
-
# :reserved and :keywords are the CodeRay 0.9.8 and 1.0.0 respectively
-
# classifications of "super", "next", "return", etc.
-
1
STATEMENT_END_TOKENS = IGNORE_TOKENS + [:regexp, :integer, :float, :keyword,
-
:delimiter, :reserved]
-
-
# Collection of tokens that should appear dedented even though they
-
# don't affect the surrounding code.
-
1
MIDWAY_TOKENS = %w(when else elsif ensure rescue)
-
-
# Clean the indentation of a fragment of ruby.
-
#
-
# @param [String] str
-
# @return [String]
-
1
def self.indent(str)
-
new.indent(str)
-
end
-
-
# Get the module nesting at the given point in the given string.
-
#
-
# NOTE If the line specified contains a method definition, then the nesting
-
# at the start of the method definition is used. Otherwise the nesting from
-
# the end of the line is used.
-
#
-
# @param [String] str The ruby code to analyze
-
# @param [Fixnum] line_number The line number (starting from 1)
-
# @return [Array<String>]
-
1
def self.nesting_at(str, line_number)
-
indent = new
-
lines = str.split("\n")
-
n = line_number - 1
-
to_indent = lines[0...n] << (lines[n] || "").split("def").first(1)
-
indent.indent(to_indent.join("\n") << "\n")
-
indent.module_nesting
-
end
-
-
1
def initialize
-
8
reset
-
end
-
-
# reset internal state
-
1
def reset
-
18
@stack = []
-
18
@indent_level = ''
-
18
@heredoc_queue = []
-
18
@close_heredocs = {}
-
18
@string_start = nil
-
18
@awaiting_class = false
-
18
@module_nesting = []
-
18
self
-
end
-
-
# Indents a string and returns it. This string can either be a single line
-
# or multiple ones.
-
#
-
# @example
-
# str = <<TXT
-
# class User
-
# attr_accessor :name
-
# end
-
# TXT
-
#
-
# # This would result in the following being displayed:
-
# #
-
# # class User
-
# # attr_accessor :name
-
# # end
-
# #
-
# puts Pry::Indent.new.indent(str)
-
#
-
# @param [String] input The input string to indent.
-
# @return [String] The indented version of +input+.
-
#
-
1
def indent(input)
-
24
output = ''
-
24
prefix = indent_level
-
-
24
input.lines.each do |line|
-
-
13
if in_string?
-
tokens = tokenize("#{open_delimiters_line}\n#{line}")
-
tokens = tokens.drop_while{ |token, type| !(String === token && token.include?("\n")) }
-
previously_in_string = true
-
else
-
13
tokens = tokenize(line)
-
13
previously_in_string = false
-
end
-
-
13
before, after = indentation_delta(tokens)
-
-
14
before.times{ prefix.sub! SPACES, '' }
-
13
new_prefix = prefix + SPACES * after
-
-
13
line = prefix + line.lstrip unless previously_in_string
-
-
13
output += line
-
-
13
prefix = new_prefix
-
end
-
-
24
@indent_level = prefix
-
-
24
return output
-
end
-
-
# Get the indentation for the start of the next line.
-
#
-
# This is what's used between the prompt and the cursor in pry.
-
#
-
# @return String The correct number of spaces
-
#
-
1
def current_prefix
-
24
in_string? ? '' : indent_level
-
end
-
-
# Get the change in indentation indicated by the line.
-
#
-
# By convention, you remove indent from the line containing end tokens,
-
# but add indent to the line *after* that which contains the start tokens.
-
#
-
# This method returns a pair, where the first number is the number of closings
-
# on this line (i.e. the number of indents to remove before the line) and the
-
# second is the number of openings (i.e. the number of indents to add after
-
# this line)
-
#
-
# @param [Array] tokens A list of tokens to scan.
-
# @return [Array[Integer]]
-
#
-
1
def indentation_delta(tokens)
-
-
# We need to keep track of whether we've seen a "for" on this line because
-
# if the line ends with "do" then that "do" should be discounted (i.e. we're
-
# only opening one level not two) To do this robustly we want to keep track
-
# of the indent level at which we saw the for, so we can differentiate
-
# between "for x in [1,2,3] do" and "for x in ([1,2,3].map do" properly
-
13
seen_for_at = []
-
-
# When deciding whether an "if" token is the start of a multiline statement,
-
# or just the middle of a single-line if statement, we just look at the
-
# preceding token, which is tracked here.
-
13
last_token, last_kind = [nil, nil]
-
-
# delta keeps track of the total difference from the start of each line after
-
# the given token, 0 is just the level at which the current line started for
-
# reference.
-
13
remove_before, add_after = [0, 0]
-
-
# If the list of tokens contains a matching closing token the line should
-
# not be indented (and thus we should return true).
-
13
tokens.each do |token, kind|
-
146
is_singleline_if = (SINGLELINE_TOKENS.include?(token)) && end_of_statement?(last_token, last_kind)
-
146
is_optional_do = (token == "do" && seen_for_at.include?(add_after - 1))
-
-
146
last_token, last_kind = token, kind unless kind == :space
-
146
next if IGNORE_TOKENS.include?(kind)
-
-
84
track_module_nesting(token, kind)
-
-
84
seen_for_at << add_after if OPTIONAL_DO_TOKENS.include?(token)
-
-
84
if kind == :delimiter
-
10
track_delimiter(token)
-
elsif OPEN_TOKENS.keys.include?(token) && !is_optional_do && !is_singleline_if
-
14
@stack << token
-
14
add_after += 1
-
elsif token == OPEN_TOKENS[@stack.last]
-
14
popped = @stack.pop
-
14
track_module_nesting_end(popped)
-
14
if add_after == 0
-
1
remove_before += 1
-
else
-
13
add_after -= 1
-
end
-
elsif MIDWAY_TOKENS.include?(token)
-
if add_after == 0
-
remove_before += 1
-
add_after += 1
-
end
-
end
-
end
-
-
13
return [remove_before, add_after]
-
end
-
-
# If the code just before an "if" or "while" token on a line looks like the end of a statement,
-
# then we want to treat that "if" as a singleline, not multiline statement.
-
1
def end_of_statement?(last_token, last_kind)
-
(last_token =~ /^[)\]}\/]$/ || STATEMENT_END_TOKENS.include?(last_kind))
-
end
-
-
# Are we currently in the middle of a string literal.
-
#
-
# This is used to determine whether to re-indent a given line, we mustn't re-indent
-
# within string literals because to do so would actually change the value of the
-
# String!
-
#
-
# @return Boolean
-
1
def in_string?
-
37
!open_delimiters.empty?
-
end
-
-
# Given a string of Ruby code, use CodeRay to export the tokens.
-
#
-
# @param [String] string The Ruby to lex
-
# @return [Array] An Array of pairs of [token_value, token_type]
-
1
def tokenize(string)
-
13
tokens = CodeRay.scan(string, :ruby)
-
13
tokens = tokens.tokens.each_slice(2) if tokens.respond_to?(:tokens) # Coderay 1.0.0
-
13
tokens.to_a
-
end
-
-
# Update the internal state about what kind of strings are open.
-
#
-
# Most of the complication here comes from the fact that HEREDOCs can be nested. For
-
# normal strings (which can't be nested) we assume that CodeRay correctly pairs
-
# open-and-close delimiters so we don't bother checking what they are.
-
#
-
# @param [String] token The token (of type :delimiter)
-
1
def track_delimiter(token)
-
10
case token
-
when /^<<-(["'`]?)(.*)\\1/
-
@heredoc_queue << token
-
@close_heredocs[token] = /^\s*$2/
-
when @close_heredocs[@heredoc_queue.first]
-
@heredoc_queue.shift
-
else
-
10
if @string_start
-
5
@string_start = nil
-
else
-
5
@string_start = token
-
end
-
end
-
end
-
-
# All the open delimiters, in the order that they first appeared.
-
#
-
# @return [String]
-
1
def open_delimiters
-
61
@heredoc_queue + [@string_start].compact
-
end
-
-
# Return a string which restores the CodeRay string status to the correct value by
-
# opening HEREDOCs and strings.
-
#
-
# @return String
-
1
def open_delimiters_line
-
"puts #{open_delimiters.join(", ")}"
-
end
-
-
# Update the internal state relating to module nesting.
-
#
-
# It's responsible for adding to the @module_nesting array, which looks
-
# something like:
-
#
-
# [ ["class", "Foo"], ["module", "Bar::Baz"], ["class <<", "self"] ]
-
#
-
# A nil value in the @module_nesting array happens in two places: either
-
# when @awaiting_class is true and we're still waiting for the string to
-
# fill that space, or when a parse was rejected.
-
#
-
# At the moment this function is quite restricted about what formats it will
-
# parse, for example we disallow expressions after the class keyword. This
-
# could maybe be improved in the future.
-
#
-
# @param [String] token a token from Coderay
-
# @param [Symbol] kind the kind of that token
-
1
def track_module_nesting(token, kind)
-
84
if kind == :keyword && (token == "class" || token == "module")
-
@module_nesting << [token, nil]
-
@awaiting_class = true
-
84
elsif @awaiting_class
-
if kind == :operator && token == "<<" && @module_nesting.last[0] == "class"
-
@module_nesting.last[0] = "class <<"
-
@awaiting_class = true
-
elsif kind == :class && token =~ /\A(self|[A-Z:][A-Za-z0-9_:]*)\z/
-
@module_nesting.last[1] = token if kind == :class
-
@awaiting_class = false
-
else
-
# leave @module_nesting[-1]
-
@awaiting_class = false
-
end
-
end
-
end
-
-
# Update the internal state relating to module nesting on 'end'.
-
#
-
# If the current 'end' pairs up with a class or a module then we should
-
# pop an array off of @module_nesting
-
#
-
# @param [String] token a token from Coderay
-
# @param [Symbol] kind the kind of that token
-
1
def track_module_nesting_end(token, kind=:keyword)
-
14
if kind == :keyword && (token == "class" || token == "module")
-
@module_nesting.pop
-
end
-
end
-
-
# Return a list of strings which can be used to re-construct the Module.nesting at
-
# the current point in the file.
-
#
-
# Returns nil if the syntax of the file was not recognizable.
-
#
-
# @return [Array<String>]
-
1
def module_nesting
-
@module_nesting.map do |(kind, token)|
-
raise UnparseableNestingError, @module_nesting.inspect if token.nil?
-
-
"#{kind} #{token}"
-
end
-
end
-
-
# Return a string which, when printed, will rewrite the previous line with
-
# the correct indentation. Mostly useful for fixing 'end'.
-
#
-
# @param [String] prompt The user's prompt
-
# @param [String] code The code the user just typed in.
-
# @param [Fixnum] overhang (0) The number of chars to erase afterwards (i.e.,
-
# the difference in length between the old line and the new one).
-
# @return [String]
-
1
def correct_indentation(prompt, code, overhang=0)
-
prompt = prompt.delete("\001\002")
-
line_to_measure = Pry::Helpers::Text.strip_color(prompt) << code
-
whitespace = ' ' * overhang
-
-
_, cols = Terminal.screen_size
-
-
cols = cols.to_i
-
lines = (cols != 0 ? (line_to_measure.length / cols + 1) : 1).to_i
-
-
if Pry::Helpers::BaseHelpers.windows_ansi?
-
move_up = "\e[#{lines}F"
-
move_down = "\e[#{lines}E"
-
else
-
move_up = "\e[#{lines}A\e[0G"
-
move_down = "\e[#{lines}B\e[0G"
-
end
-
-
"#{move_up}#{prompt}#{colorize_code(code)}#{whitespace}#{move_down}"
-
end
-
end
-
end
-
1
require 'thread'
-
-
1
class Pry
-
# There is one InputLock per input (such as STDIN) as two REPLs on the same
-
# input makes things delirious. InputLock serializes accesses to the input so
-
# that threads to not conflict with each other. The latest thread to request
-
# ownership of the input wins.
-
1
class InputLock
-
1
class Interrupt < Exception; end
-
-
1
class << self
-
1
attr_accessor :input_locks
-
1
attr_accessor :global_lock
-
end
-
-
1
self.input_locks = {}
-
1
self.global_lock = Mutex.new
-
-
1
def self.for(input)
-
# XXX This method leaks memory, as we never unregister an input once we
-
# are done with it. Fortunately, the leak is tiny (or so we hope). In
-
# usual scenarios, we would leak the StringIO that is passed to be
-
# evaluated from the command line.
-
27
global_lock.synchronize do
-
27
input_locks[input] ||= Pry::InputLock.new
-
end
-
end
-
-
1
def initialize
-
1
@mutex = Mutex.new
-
1
@cond = ConditionVariable.new
-
1
@owners = []
-
1
@interruptible = false
-
end
-
-
# Adds ourselves to the ownership list. The last one in the list may access
-
# the input through interruptible_region().
-
1
def __with_ownership(&block)
-
1
@mutex.synchronize do
-
# Three cases:
-
# 1) There are no owners, in this case we are good to go.
-
# 2) The current owner of the input is not reading the input (it might
-
# just be evaluating some ruby that the user typed).
-
# The current owner will figure out that it cannot go back to reading
-
# the input since we are adding ourselves to the @owners list, which
-
# in turns makes us the current owner.
-
# 3) The owner of the input is in the interruptible region, reading from
-
# the input. It's safe to send an Interrupt exception to interrupt
-
# the owner. It will then proceed like in case 2).
-
# We wait until the owner sets the interruptible flag back
-
# to false, meaning that he's out of the interruptible region.
-
# Note that the owner may receive multiple interrupts since, but that
-
# should be okay (and trying to avoid it is futile anyway).
-
1
while @interruptible
-
@owners.last.raise Interrupt
-
@cond.wait(@mutex)
-
end
-
1
@owners << Thread.current
-
end
-
-
1
block.call
-
-
ensure
-
1
@mutex.synchronize do
-
# We are releasing any desire to have the input ownership by removing
-
# ourselves from the list.
-
1
@owners.delete(Thread.current)
-
-
# We need to wake up the thread at the end of the @owners list, but
-
# sadly Ruby doesn't allow us to choose which one we wake up, so we wake
-
# them all up.
-
1
@cond.broadcast
-
end
-
end
-
-
1
def with_ownership(&block)
-
# If we are in a nested with_ownership() call (nested pry context), we do nothing.
-
6
nested = @mutex.synchronize { @owners.include?(Thread.current) }
-
3
nested ? block.call : __with_ownership(&block)
-
end
-
-
1
def enter_interruptible_region
-
24
@mutex.synchronize do
-
# We patiently wait until we are the owner. This may happen as another
-
# thread calls with_ownership() because of a binding.pry happening in
-
# another thread.
-
24
@cond.wait(@mutex) until @owners.last == Thread.current
-
-
# We are the legitimate owner of the input. We mark ourselves as
-
# interruptible, so other threads can send us an Interrupt exception
-
# while we are blocking from reading the input.
-
24
@interruptible = true
-
end
-
end
-
-
1
def leave_interruptible_region
-
24
@mutex.synchronize do
-
# We check if we are still the owner, because we could have received an
-
# Interrupt right after the following @cond.broadcast, making us retry.
-
24
@interruptible = false if @owners.last == Thread.current
-
24
@cond.broadcast
-
end
-
rescue Interrupt
-
# We need to guard against a spurious interrupt delivered while we are
-
# trying to acquire the lock (the rescue block is no longer in our scope).
-
retry
-
end
-
-
1
def interruptible_region(&block)
-
24
enter_interruptible_region
-
-
# XXX Note that there is a chance that we get the interrupt right after
-
# the readline call succeeded, but we'll never know, and we will retry the
-
# call, discarding that piece of input.
-
24
block.call
-
-
rescue Interrupt
-
# We were asked to back off. The one requesting the interrupt will be
-
# waiting on the conditional for the interruptible flag to change to false.
-
# Note that there can be some inefficiency, as we could immediately
-
# succeed in enter_interruptible_region(), even before the one requesting
-
# the ownership has the chance to register itself as an owner.
-
# To mitigate the issue, we sleep a little bit.
-
leave_interruptible_region
-
sleep 0.01
-
retry
-
-
ensure
-
24
leave_interruptible_region
-
end
-
end
-
end
-
1
class Pry::Inspector
-
1
MAP = {
-
"default" => {
-
value: Pry::DEFAULT_PRINT,
-
description: <<-DESCRIPTION.each_line.map(&:lstrip!)
-
The default Pry inspector. It has paging and color support, and uses
-
pretty_inspect when printing an object.
-
DESCRIPTION
-
},
-
-
"simple" => {
-
value: Pry::SIMPLE_PRINT,
-
description: <<-DESCRIPTION.each_line.map(&:lstrip)
-
A simple inspector that uses #puts and #inspect when printing an
-
object. It has no pager, color, or pretty_inspect support.
-
DESCRIPTION
-
},
-
-
"clipped" => {
-
value: Pry::CLIPPED_PRINT,
-
description: <<-DESCRIPTION.each_line.map(&:lstrip)
-
The clipped inspector has the same features as the 'simple' inspector
-
but prints large objects as a smaller string.
-
DESCRIPTION
-
}
-
}
-
end
-
#
-
# {Pry::LastException} is a proxy class who wraps an Exception object for
-
# {Pry#last_exception}. it extends the exception object with methods that
-
# help pry commands be useful.
-
#
-
# the original exception object is not modified and method calls are forwarded
-
# to the wrapped exception object.
-
#
-
1
class Pry::LastException < BasicObject
-
1
attr_accessor :bt_index
-
-
1
def initialize(e)
-
2
@e = e
-
2
@bt_index = 0
-
2
@file, @line = bt_source_location_for(0)
-
end
-
-
1
def method_missing(name, *args, &block)
-
2
if @e.respond_to?(name)
-
2
@e.public_send(name, *args, &block)
-
else
-
super
-
end
-
end
-
-
1
def respond_to_missing?(name, include_private = false)
-
@e.respond_to?(name)
-
end
-
-
#
-
# @return [String]
-
# returns the path to a file for the current backtrace. see {#bt_index}.
-
#
-
1
def file
-
@file
-
end
-
-
#
-
# @return [Fixnum]
-
# returns the line for the current backtrace. see {#bt_index}.
-
#
-
1
def line
-
@line
-
end
-
-
# @return [Exception]
-
# returns the wrapped exception
-
#
-
1
def wrapped_exception
-
24
@e
-
end
-
-
1
def bt_source_location_for(index)
-
2
backtrace[index] =~ /(.*):(\d+)/
-
2
[$1, $2.to_i]
-
end
-
-
1
def inc_bt_index
-
@bt_index = (@bt_index + 1) % backtrace.size
-
end
-
end
-
1
require 'pry/helpers/documentation_helpers'
-
-
1
class Pry
-
1
class << self
-
# If the given object is a `Pry::Method`, return it unaltered. If it's
-
# anything else, return it wrapped in a `Pry::Method` instance.
-
1
def Method(obj)
-
4
if obj.is_a? Pry::Method
-
4
obj
-
else
-
Pry::Method.new(obj)
-
end
-
end
-
end
-
-
# This class wraps the normal `Method` and `UnboundMethod` classes
-
# to provide extra functionality useful to Pry.
-
1
class Method
-
1
require 'pry/method/weird_method_locator'
-
1
require 'pry/method/disowned'
-
1
require 'pry/method/patcher'
-
-
1
extend Helpers::BaseHelpers
-
1
include Helpers::BaseHelpers
-
1
include Helpers::DocumentationHelpers
-
1
include CodeObject::Helpers
-
-
1
class << self
-
# Given a string representing a method name and optionally a binding to
-
# search in, find and return the requested method wrapped in a `Pry::Method`
-
# instance.
-
#
-
# @param [String] name The name of the method to retrieve.
-
# @param [Binding] target The context in which to search for the method.
-
# @param [Hash] options
-
# @option options [Boolean] :instance Look for an instance method if `name` doesn't
-
# contain any context.
-
# @option options [Boolean] :methods Look for a bound/singleton method if `name` doesn't
-
# contain any context.
-
# @return [Pry::Method, nil] A `Pry::Method` instance containing the requested
-
# method, or `nil` if name is `nil` or no method could be located matching the parameters.
-
1
def from_str(name, target=TOPLEVEL_BINDING, options={})
-
if name.nil?
-
nil
-
elsif name.to_s =~ /(.+)\#(\S+)\Z/
-
context, meth_name = $1, $2
-
from_module(target.eval(context), meth_name, target)
-
elsif name.to_s =~ /(.+)(\[\])\Z/
-
context, meth_name = $1, $2
-
from_obj(target.eval(context), meth_name, target)
-
elsif name.to_s =~ /(.+)(\.|::)(\S+)\Z/
-
context, meth_name = $1, $3
-
from_obj(target.eval(context), meth_name, target)
-
elsif options[:instance]
-
from_module(target.eval("self"), name, target)
-
elsif options[:methods]
-
from_obj(target.eval("self"), name, target)
-
else
-
from_str(name, target, :instance => true) or
-
from_str(name, target, :methods => true)
-
end
-
-
rescue Pry::RescuableException
-
nil
-
end
-
-
# Given a `Binding`, try to extract the `::Method` it originated from and
-
# use it to instantiate a `Pry::Method`. Return `nil` if this isn't
-
# possible.
-
#
-
# @param [Binding] b
-
# @return [Pry::Method, nil]
-
#
-
1
def from_binding(b)
-
4
meth_name = b.eval('::Kernel.__method__')
-
4
if [:__script__, nil].include?(meth_name)
-
nil
-
else
-
4
method = begin
-
4
if Object === b.eval('self')
-
4
new(Kernel.instance_method(:method).bind(b.eval("self")).call(meth_name))
-
else
-
new(b.eval('class << self; self; end.instance_method(::Kernel.__method__).bind(self)'))
-
end
-
rescue NameError, NoMethodError
-
Disowned.new(b.eval('self'), meth_name.to_s)
-
end
-
-
4
if WeirdMethodLocator.weird_method?(method, b)
-
WeirdMethodLocator.new(method, b).get_method || method
-
else
-
4
method
-
end
-
end
-
end
-
-
# In order to support 2.0 Refinements we need to look up methods
-
# inside the relevant Binding.
-
# @param [Object] obj The owner/receiver of the method.
-
# @param [Symbol] method_name The name of the method.
-
# @param [Symbol] method_type The type of method: :method or :instance_method
-
# @param [Binding] target The binding where the method is looked up.
-
# @return [Method, UnboundMethod] The 'refined' method object.
-
1
def lookup_method_via_binding(obj, method_name, method_type, target=TOPLEVEL_BINDING)
-
Pry.current[:obj] = obj
-
Pry.current[:name] = method_name
-
receiver = obj.is_a?(Module) ? "Module" : "Kernel"
-
target.eval("::#{receiver}.instance_method(:#{method_type}).bind(Pry.current[:obj]).call(Pry.current[:name])")
-
ensure
-
Pry.current[:obj] = Pry.current[:name] = nil
-
end
-
-
# Given a `Class` or `Module` and the name of a method, try to
-
# instantiate a `Pry::Method` containing the instance method of
-
# that name. Return `nil` if no such method exists.
-
#
-
# @param [Class, Module] klass
-
# @param [String] name
-
# @param [Binding] target The binding where the method is looked up.
-
# @return [Pry::Method, nil]
-
1
def from_class(klass, name, target=TOPLEVEL_BINDING)
-
new(lookup_method_via_binding(klass, name, :instance_method, target)) rescue nil
-
end
-
1
alias from_module from_class
-
-
# Given an object and the name of a method, try to instantiate
-
# a `Pry::Method` containing the method of that name bound to
-
# that object. Return `nil` if no such method exists.
-
#
-
# @param [Object] obj
-
# @param [String] name
-
# @param [Binding] target The binding where the method is looked up.
-
# @return [Pry::Method, nil]
-
1
def from_obj(obj, name, target=TOPLEVEL_BINDING)
-
new(lookup_method_via_binding(obj, name, :method, target)) rescue nil
-
end
-
-
# Get all of the instance methods of a `Class` or `Module`
-
# @param [Class,Module] klass
-
# @param [Boolean] include_super Whether to include methods from ancestors.
-
# @return [Array[Pry::Method]]
-
1
def all_from_class(klass, include_super=true)
-
%w(public protected private).map do |visibility|
-
safe_send(klass, :"#{visibility}_instance_methods", include_super).map do |method_name|
-
new(safe_send(klass, :instance_method, method_name), :visibility => visibility.to_sym)
-
end
-
end.flatten(1)
-
end
-
-
#
-
# Get all of the methods on an `Object`
-
#
-
# @param [Object] obj
-
#
-
# @param [Boolean] include_super
-
# indicates whether or not to include methods from ancestors.
-
#
-
# @return [Array[Pry::Method]]
-
#
-
1
def all_from_obj(obj, include_super=true)
-
all_from_class(singleton_class_of(obj), include_super)
-
end
-
-
#
-
# @deprecated
-
# please use {#all_from_obj} instead.
-
# the `method_type` argument is ignored.
-
#
-
1
def all_from_common(obj, method_type = nil, include_super=true)
-
all_from_obj(obj, include_super)
-
end
-
-
# Get every `Class` and `Module`, in order, that will be checked when looking
-
# for an instance method to call on this object.
-
# @param [Object] obj
-
# @return [Array[Class, Module]]
-
1
def resolution_order(obj)
-
if Class === obj
-
singleton_class_resolution_order(obj) + instance_resolution_order(Class)
-
else
-
klass = singleton_class_of(obj) rescue obj.class
-
instance_resolution_order(klass)
-
end
-
end
-
-
# Get every `Class` and `Module`, in order, that will be checked when looking
-
# for methods on instances of the given `Class` or `Module`.
-
# This does not treat singleton classes of classes specially.
-
# @param [Class, Module] klass
-
# @return [Array[Class, Module]]
-
1
def instance_resolution_order(klass)
-
# include klass in case it is a singleton class,
-
([klass] + Pry::Method.safe_send(klass, :ancestors)).uniq
-
end
-
-
1
def method_definition?(name, definition_line)
-
singleton_method_definition?(name, definition_line) ||
-
instance_method_definition?(name, definition_line)
-
end
-
-
1
def singleton_method_definition?(name, definition_line)
-
/^define_singleton_method\(?\s*[:\"\']#{Regexp.escape(name)}|^def\s*self\.#{Regexp.escape(name)}/ =~ definition_line.strip
-
end
-
-
1
def instance_method_definition?(name, definition_line)
-
/^define_method\(?\s*[:\"\']#{Regexp.escape(name)}|^def\s*#{Regexp.escape(name)}/ =~ definition_line.strip
-
end
-
-
# Get the singleton classes of superclasses that could define methods on
-
# the given class object, and any modules they include.
-
# If a module is included at multiple points in the ancestry, only
-
# the lowest copy will be returned.
-
1
def singleton_class_resolution_order(klass)
-
ancestors = Pry::Method.safe_send(klass, :ancestors)
-
resolution_order = ancestors.grep(Class).map do |anc|
-
[singleton_class_of(anc), *singleton_class_of(anc).included_modules]
-
end.flatten(1)
-
-
resolution_order.reverse.uniq.reverse - Class.included_modules
-
end
-
-
1
def singleton_class_of(obj)
-
begin
-
class << obj; self; end
-
rescue TypeError # can't define singleton. Fixnum, Symbol, Float, ...
-
obj.class
-
end
-
end
-
end
-
-
# A new instance of `Pry::Method` wrapping the given `::Method`, `UnboundMethod`, or `Proc`.
-
#
-
# @param [::Method, UnboundMethod, Proc] method
-
# @param [Hash] known_info Can be used to pre-cache expensive to compute stuff.
-
# @return [Pry::Method]
-
1
def initialize(method, known_info={})
-
4
@method = method
-
4
@visibility = known_info[:visibility]
-
end
-
-
# Get the name of the method as a String, regardless of the underlying Method#name type.
-
# @return [String]
-
1
def name
-
4
@method.name.to_s
-
end
-
-
# Get the owner of the method as a Pry::Module
-
# @return [Pry::Module]
-
1
def wrapped_owner
-
4
@wrapped_owner ||= Pry::WrappedModule.new(owner)
-
end
-
-
# Get underlying object wrapped by this Pry::Method instance
-
# @return [Method, UnboundMethod, Proc]
-
1
def wrapped
-
@method
-
end
-
-
# Is the method undefined? (aka `Disowned`)
-
# @return [Boolean] false
-
1
def undefined?
-
false
-
end
-
-
# Get the name of the method including the class on which it was defined.
-
# @example
-
# method(:puts).method_name
-
# => "Kernel.puts"
-
# @return [String]
-
1
def name_with_owner
-
4
"#{wrapped_owner.method_prefix}#{name}"
-
end
-
-
# @return [String, nil] The source code of the method, or `nil` if it's unavailable.
-
1
def source
-
@source ||= case source_type
-
when :c
-
c_source
-
when :ruby
-
4
ruby_source
-
24
end
-
end
-
-
# Update the live copy of the method's source.
-
1
def redefine(source)
-
Patcher.new(self).patch_in_ram source
-
Pry::Method(owner.instance_method(name))
-
end
-
-
# Can we get the source code for this method?
-
# @return [Boolean]
-
1
def source?
-
4
!!source
-
rescue MethodSource::SourceNotFoundError
-
false
-
end
-
-
# @return [String, nil] The documentation for the method, or `nil` if it's
-
# unavailable.
-
1
def doc
-
@doc ||= case source_type
-
when :c
-
info = pry_doc_info
-
info.docstring if info
-
when :ruby
-
get_comment_content(comment)
-
end
-
end
-
-
# @return [Symbol] The source type of the method. The options are
-
# `:ruby` for Ruby methods or `:c` for methods written in C.
-
1
def source_type
-
8
source_location.nil? ? :c : :ruby
-
end
-
-
# @return [String, nil] The name of the file the method is defined in, or
-
# `nil` if the filename is unavailable.
-
1
def source_file
-
12
if source_location.nil?
-
if !rbx? and source_type == :c
-
info = pry_doc_info
-
info.file if info
-
end
-
else
-
12
source_location.first
-
end
-
end
-
-
# @return [Fixnum, nil] The line of code in `source_file` which begins
-
# the method's definition, or `nil` if that information is unavailable.
-
1
def source_line
-
36
source_location.nil? ? nil : source_location.last
-
end
-
-
# @return [Range, nil] The range of lines in `source_file` which contain
-
# the method's definition, or `nil` if that information is unavailable.
-
1
def source_range
-
16
source_location.nil? ? nil : (source_line)..(source_line + source.lines.count - 1)
-
end
-
-
# @return [Symbol] The visibility of the method. May be `:public`,
-
# `:protected`, or `:private`.
-
1
def visibility
-
@visibility ||= if owner.public_instance_methods.any? { |m| m.to_s == name }
-
:public
-
elsif owner.protected_instance_methods.any? { |m| m.to_s == name }
-
:protected
-
elsif owner.private_instance_methods.any? { |m| m.to_s == name }
-
:private
-
else
-
:none
-
end
-
end
-
-
# @return [String] A representation of the method's signature, including its
-
# name and parameters. Optional and "rest" parameters are marked with `*`
-
# and block parameters with `&`. If the parameter names are unavailable,
-
# they're given numbered names instead.
-
# Paraphrased from `awesome_print` gem.
-
1
def signature
-
if respond_to?(:parameters)
-
args = parameters.inject([]) do |arr, (type, name)|
-
name ||= (type == :block ? 'block' : "arg#{arr.size + 1}")
-
arr << case type
-
when :req then name.to_s
-
when :opt then "#{name}=?"
-
when :rest then "*#{name}"
-
when :block then "&#{name}"
-
else '?'
-
end
-
end
-
else
-
args = (1..arity.abs).map { |i| "arg#{i}" }
-
args[-1] = "*#{args[-1]}" if arity < 0
-
end
-
-
"#{name}(#{args.join(', ')})"
-
end
-
-
# @return [Pry::Method, nil] The wrapped method that is called when you
-
# use "super" in the body of this method.
-
1
def super(times=1)
-
if UnboundMethod === @method
-
sup = super_using_ancestors(Pry::Method.instance_resolution_order(owner), times)
-
else
-
sup = super_using_ancestors(Pry::Method.resolution_order(receiver), times)
-
sup &&= sup.bind(receiver)
-
end
-
Pry::Method.new(sup) if sup
-
end
-
-
# @return [String, nil] The original name the method was defined under,
-
# before any aliasing, or `nil` if it can't be determined.
-
1
def original_name
-
return nil if source_type != :ruby
-
method_name_from_first_line(source.lines.first)
-
end
-
-
# @return [Boolean] Was the method defined outside a source file?
-
1
def dynamically_defined?
-
!!(source_file and source_file =~ /(\(.*\))|<.*>/)
-
end
-
-
# @return [Boolean] Whether the method is unbound.
-
1
def unbound_method?
-
is_a?(::UnboundMethod)
-
end
-
-
# @return [Boolean] Whether the method is bound.
-
1
def bound_method?
-
is_a?(::Method)
-
end
-
-
# @return [Boolean] Whether the method is a singleton method.
-
1
def singleton_method?
-
wrapped_owner.singleton_class?
-
end
-
-
# @return [Boolean] Was the method defined within the Pry REPL?
-
1
def pry_method?
-
source_file == Pry.eval_path
-
end
-
-
# @return [Array<String>] All known aliases for the method.
-
1
def aliases
-
owner = @method.owner
-
# Avoid using `to_sym` on {Method#name}, which returns a `String`, because
-
# it won't be garbage collected.
-
name = @method.name
-
-
all_methods_to_compare = owner.instance_methods | owner.private_instance_methods
-
alias_list = all_methods_to_compare.combination(2).select do |pair|
-
pair.include?(name) &&
-
owner.instance_method(pair.first) == owner.instance_method(pair.last)
-
end.flatten
-
alias_list.delete(name)
-
-
alias_list.map(&:to_s)
-
end
-
-
# @return [Boolean] Is the method definitely an alias?
-
1
def alias?
-
name != original_name
-
end
-
-
# @return [Boolean]
-
1
def ==(obj)
-
if obj.is_a? Pry::Method
-
obj == @method
-
else
-
@method == obj
-
end
-
end
-
-
# @param [Class] klass
-
# @return [Boolean]
-
1
def is_a?(klass)
-
4
klass == Pry::Method or @method.is_a?(klass)
-
end
-
1
alias kind_of? is_a?
-
-
# @param [String, Symbol] method_name
-
# @return [Boolean]
-
1
def respond_to?(method_name)
-
super or @method.respond_to?(method_name)
-
end
-
-
# Delegate any unknown calls to the wrapped method.
-
1
def method_missing(method_name, *args, &block)
-
128
@method.send(method_name, *args, &block)
-
end
-
-
1
def comment
-
Pry::Code.from_file(source_file).comment_describing(source_line)
-
end
-
-
1
private
-
-
# @return [YARD::CodeObjects::MethodObject]
-
# @raise [CommandError] when the method can't be found or `pry-doc` isn't installed.
-
1
def pry_doc_info
-
if Pry.config.has_pry_doc
-
Pry::MethodInfo.info_for(@method) or raise CommandError, "Cannot locate this method: #{name}. (source_location returns nil)"
-
else
-
fail_msg = "Cannot locate this method: #{name}."
-
if mri?
-
fail_msg += ' Try `gem-install pry-doc` to get access to Ruby Core documentation.'
-
end
-
raise CommandError, fail_msg
-
end
-
end
-
-
# @param [Class, Module] ancestors The ancestors to investigate
-
# @return [Method] The unwrapped super-method
-
1
def super_using_ancestors(ancestors, times=1)
-
next_owner = self.owner
-
times.times do
-
i = ancestors.index(next_owner) + 1
-
while ancestors[i] && !(ancestors[i].method_defined?(name) || ancestors[i].private_method_defined?(name))
-
i += 1
-
end
-
next_owner = ancestors[i] or return nil
-
end
-
-
safe_send(next_owner, :instance_method, name) rescue nil
-
end
-
-
# @param [String] first_ln The first line of a method definition.
-
# @return [String, nil]
-
1
def method_name_from_first_line(first_ln)
-
return nil if first_ln.strip !~ /^def /
-
-
tokens = CodeRay.scan(first_ln, :ruby)
-
tokens = tokens.tokens.each_slice(2) if tokens.respond_to?(:tokens)
-
tokens.each_cons(2) do |t1, t2|
-
if t2.last == :method || t2.last == :ident && t1 == [".", :operator]
-
return t2.first
-
end
-
end
-
-
nil
-
end
-
-
1
def c_source
-
info = pry_doc_info
-
if info and info.source
-
strip_comments_from_c_code(info.source)
-
end
-
end
-
-
1
def ruby_source
-
# clone of MethodSource.source_helper that knows to use our
-
# hacked version of source_location for rbx core methods, and
-
# our input buffer for methods defined in (pry)
-
4
file, line = *source_location
-
4
raise SourceNotFoundError, "Could not locate source for #{name_with_owner}!" unless file
-
-
4
begin
-
4
code = Pry::Code.from_file(file).expression_at(line)
-
rescue SyntaxError => e
-
raise MethodSource::SourceNotFoundError.new(e.message)
-
end
-
4
strip_leading_whitespace(code)
-
end
-
end
-
end
-
1
class Pry
-
1
class Method
-
# A Disowned Method is one that's been removed from the class on which it was defined.
-
#
-
# e.g.
-
# class C
-
# def foo
-
# C.send(:undefine_method, :foo)
-
# Pry::Method.from_binding(binding)
-
# end
-
# end
-
#
-
# In this case we assume that the "owner" is the singleton class of the receiver.
-
#
-
# This occurs mainly in Sinatra applications.
-
1
class Disowned < Method
-
1
attr_reader :receiver, :name
-
-
# Create a new Disowned method.
-
#
-
# @param [Object] receiver
-
# @param [String] method_name
-
1
def initialize(receiver, method_name, binding=nil)
-
@receiver, @name = receiver, method_name
-
end
-
-
# Is the method undefined? (aka `Disowned`)
-
# @return [Boolean] true
-
1
def undefined?
-
true
-
end
-
-
# Can we get the source for this method?
-
# @return [Boolean] false
-
1
def source?
-
false
-
end
-
-
# Get the hypothesized owner of the method.
-
#
-
# @return [Object]
-
1
def owner
-
class << receiver; self; end
-
end
-
-
# Raise a more useful error message instead of trying to forward to nil.
-
1
def method_missing(meth_name, *args, &block)
-
raise "Cannot call '#{meth_name}' on an undef'd method." if method(:name).respond_to?(meth_name)
-
Object.instance_method(:method_missing).bind(self).call(meth_name, *args, &block)
-
end
-
end
-
end
-
end
-
1
class Pry
-
1
class Method
-
1
class Patcher
-
1
attr_accessor :method
-
-
1
@@source_cache = {}
-
-
1
def initialize(method)
-
@method = method
-
end
-
-
1
def self.code_for(filename)
-
3
@@source_cache[filename]
-
end
-
-
# perform the patch
-
1
def patch_in_ram(source)
-
if method.alias?
-
with_method_transaction do
-
redefine source
-
end
-
else
-
redefine source
-
end
-
end
-
-
1
private
-
-
1
def redefine(source)
-
@@source_cache[cache_key] = source
-
TOPLEVEL_BINDING.eval wrap(source), cache_key
-
end
-
-
1
def cache_key
-
"pry-redefined(0x#{method.owner.object_id.to_s(16)}##{method.name})"
-
end
-
-
# Run some code ensuring that at the end target#meth_name will not have changed.
-
#
-
# When we're redefining aliased methods we will overwrite the method at the
-
# unaliased name (so that super continues to work). By wrapping that code in a
-
# transation we make that not happen, which means that alias_method_chains, etc.
-
# continue to work.
-
#
-
# @param [String] meth_name The method name before aliasing
-
# @param [Module] target The owner of the method
-
-
1
def with_method_transaction
-
temp_name = "__pry_#{method.original_name}__"
-
method = self.method
-
method.owner.class_eval do
-
alias_method temp_name, method.original_name
-
yield
-
alias_method method.name, method.original_name
-
alias_method method.original_name, temp_name
-
end
-
-
ensure
-
method.send(:remove_method, temp_name) rescue nil
-
end
-
-
# Update the definition line so that it can be eval'd directly on the Method's
-
# owner instead of from the original context.
-
#
-
# In particular this takes `def self.foo` and turns it into `def foo` so that we
-
# don't end up creating the method on the singleton class of the singleton class
-
# by accident.
-
#
-
# This is necessarily done by String manipulation because we can't find out what
-
# syntax is needed for the argument list by ruby-level introspection.
-
#
-
# @param [String] line The original definition line. e.g. def self.foo(bar, baz=1)
-
# @return [String] The new definition line. e.g. def foo(bar, baz=1)
-
1
def definition_for_owner(line)
-
if line =~ /\Adef (?:.*?\.)?#{Regexp.escape(method.original_name)}(?=[\(\s;]|$)/
-
"def #{method.original_name}#{$'}"
-
else
-
raise CommandError, "Could not find original `def #{method.original_name}` line to patch."
-
end
-
end
-
-
# Apply wrap_for_owner and wrap_for_nesting successively to `source`
-
# @param [String] source
-
# @return [String] The wrapped source.
-
1
def wrap(source)
-
wrap_for_nesting(wrap_for_owner(source))
-
end
-
-
# Update the source code so that when it has the right owner when eval'd.
-
#
-
# This (combined with definition_for_owner) is backup for the case that
-
# wrap_for_nesting fails, to ensure that the method will stil be defined in
-
# the correct place.
-
#
-
# @param [String] source The source to wrap
-
# @return [String]
-
1
def wrap_for_owner(source)
-
Pry.current[:pry_owner] = method.owner
-
owner_source = definition_for_owner(source)
-
visibility_fix = "#{method.visibility.to_s} #{method.name.to_sym.inspect}"
-
"Pry.current[:pry_owner].class_eval do; #{owner_source}\n#{visibility_fix}\nend"
-
end
-
-
# Update the new source code to have the correct Module.nesting.
-
#
-
# This method uses syntactic analysis of the original source file to determine
-
# the new nesting, so that we can tell the difference between:
-
#
-
# class A; def self.b; end; end
-
# class << A; def b; end; end
-
#
-
# The resulting code should be evaluated in the TOPLEVEL_BINDING.
-
#
-
# @param [String] source The source to wrap.
-
# @return [String]
-
1
def wrap_for_nesting(source)
-
nesting = Pry::Code.from_file(method.source_file).nesting_at(method.source_line)
-
-
(nesting + [source] + nesting.map{ "end" } + [""]).join(";")
-
rescue Pry::Indent::UnparseableNestingError
-
source
-
end
-
end
-
end
-
end
-
1
class Pry
-
1
class Method
-
-
# This class is responsible for locating the *real* `Pry::Method`
-
# object captured by a binding.
-
#
-
# Given a `Binding` from inside a method and a 'seed' Pry::Method object,
-
# there are primarily two situations where the seed method doesn't match
-
# the Binding:
-
# 1. The Pry::Method is from a subclass 2. The Pry::Method represents a method of the same name
-
# while the original was renamed to something else. For 1. we
-
# search vertically up the inheritance chain,
-
# and for 2. we search laterally along the object's method table.
-
#
-
# When we locate the method that matches the Binding we wrap it in
-
# Pry::Method and return it, or return nil if we fail.
-
1
class WeirdMethodLocator
-
1
class << self
-
-
# Whether the given method object matches the associated binding.
-
# If the method object does not match the binding, then it's
-
# most likely not the method captured by the binding, and we
-
# must commence a search.
-
#
-
# @param [Pry::Method] method
-
# @param [Binding] b
-
# @return [Boolean]
-
1
def normal_method?(method, b)
-
4
method && (method.source_file && method.source_range rescue false) &&
-
File.expand_path(method.source_file) == File.expand_path(b.eval('__FILE__')) &&
-
method.source_range.include?(b.eval('__LINE__'))
-
end
-
-
1
def weird_method?(method, b)
-
4
!normal_method?(method, b)
-
end
-
end
-
-
1
attr_accessor :method
-
1
attr_accessor :target
-
-
# @param [Pry::Method] method The seed method.
-
# @param [Binding] target The Binding that captures the method
-
# we want to locate.
-
1
def initialize(method, target)
-
@method, @target = method, target
-
end
-
-
# @return [Pry::Method, nil] The Pry::Method that matches the
-
# given binding.
-
1
def get_method
-
find_method_in_superclass || find_renamed_method
-
end
-
-
# @return [Boolean] Whether the Pry::Method is unrecoverable
-
# This usually happens when the method captured by the Binding
-
# has been subsequently deleted.
-
1
def lost_method?
-
!!(get_method.nil? && renamed_method_source_location)
-
end
-
-
1
private
-
-
1
def normal_method?(method)
-
self.class.normal_method?(method, target)
-
end
-
-
1
def target_self
-
target.eval('self')
-
end
-
-
1
def target_file
-
pry_file? ? target.eval('__FILE__') : File.expand_path(target.eval('__FILE__'))
-
end
-
-
1
def target_line
-
target.eval('__LINE__')
-
end
-
-
1
def pry_file?
-
Pry.eval_path == target.eval('__FILE__')
-
end
-
-
# it's possible in some cases that the method we find by this approach is a sub-method of
-
# the one we're currently in, consider:
-
#
-
# class A; def b; binding.pry; end; end
-
# class B < A; def b; super; end; end
-
#
-
# Given that we can normally find the source_range of methods, and that we know which
-
# __FILE__ and __LINE__ the binding is at, we can hope to disambiguate these cases.
-
#
-
# This obviously won't work if the source is unavaiable for some reason, or if both
-
# methods have the same __FILE__ and __LINE__, or if we're in rbx where b.eval('__LINE__')
-
# is broken.
-
#
-
# @return [Pry::Method, nil] The Pry::Method representing the
-
# superclass method.
-
1
def find_method_in_superclass
-
guess = method
-
-
while guess
-
# needs rescue if this is a Disowned method or a C method or something...
-
# TODO: Fix up the exception handling so we don't need a bare rescue
-
if normal_method?(guess)
-
return guess
-
else
-
guess = guess.super
-
end
-
end
-
-
# Uhoh... none of the methods in the chain had the right __FILE__ and __LINE__
-
# This may be caused by rbx https://github.com/rubinius/rubinius/issues/953,
-
# or other unknown circumstances (TODO: we should warn the user when this happens)
-
nil
-
end
-
-
# This is the case where the name of a method has changed
-
# (via alias_method) so we locate the Method object for the
-
# renamed method.
-
#
-
# @return [Pry::Method, nil] The Pry::Method representing the
-
# renamed method
-
1
def find_renamed_method
-
return if !valid_file?(target_file)
-
alias_name = all_methods_for(target_self).find do |v|
-
expanded_source_location(target_self.method(v).source_location) == renamed_method_source_location
-
end
-
-
alias_name && Pry::Method(target_self.method(alias_name))
-
end
-
-
1
def expanded_source_location(sl)
-
return if !sl
-
-
if pry_file?
-
sl
-
else
-
[File.expand_path(sl.first), sl.last]
-
end
-
end
-
-
# Use static analysis to locate the start of the method definition.
-
# We have the `__FILE__` and `__LINE__` from the binding and the
-
# original name of the method so we search up until we find a
-
# def/define_method, etc defining a method of the appropriate name.
-
#
-
# @return [Array<String, Fixnum>] The `source_location` of the
-
# renamed method
-
1
def renamed_method_source_location
-
return @original_method_source_location if defined?(@original_method_source_location)
-
-
source_index = lines_for_file(target_file)[0..(target_line - 1)].rindex do |v|
-
Pry::Method.method_definition?(method.name, v)
-
end
-
-
@original_method_source_location = source_index &&
-
[target_file, index_to_line_number(source_index)]
-
end
-
-
1
def index_to_line_number(index)
-
# Pry.line_buffer is 0-indexed
-
pry_file? ? index : index + 1
-
end
-
-
1
def valid_file?(file)
-
(File.exist?(file) && !File.directory?(file)) || Pry.eval_path == file
-
end
-
-
1
def lines_for_file(file)
-
@lines_for_file ||= {}
-
@lines_for_file[file] ||= if Pry.eval_path == file
-
Pry.line_buffer
-
else
-
File.readlines(file)
-
end
-
end
-
-
1
def all_methods_for(obj)
-
obj.public_methods(false) +
-
obj.private_methods(false) +
-
obj.protected_methods(false)
-
end
-
end
-
end
-
end
-
1
require 'pry/helpers/documentation_helpers'
-
1
require 'forwardable'
-
-
1
class Pry
-
1
class WrappedModule
-
-
# This class represents a single candidate for a module/class definition.
-
# It provides access to the source, documentation, line and file
-
# for a monkeypatch (reopening) of a class/module.
-
1
class Candidate
-
1
include Pry::Helpers::DocumentationHelpers
-
1
include Pry::CodeObject::Helpers
-
1
extend Forwardable
-
-
# @return [String] The file where the module definition is located.
-
1
attr_reader :file
-
1
alias_method :source_file, :file
-
-
# @return [Fixnum] The line where the module definition is located.
-
1
attr_reader :line
-
1
alias_method :source_line, :line
-
-
# Methods to delegate to associated `Pry::WrappedModule
-
# instance`.
-
1
private_delegates = [:lines_for_file, :method_candidates,
-
:yard_docs?]
-
-
1
public_delegates = [:wrapped, :module?, :class?, :name, :nonblank_name,
-
:number_of_candidates]
-
-
1
def_delegators :@wrapper, *(private_delegates + public_delegates)
-
1
private(*private_delegates)
-
1
public(*public_delegates)
-
-
# @raise [Pry::CommandError] If `rank` is out of bounds.
-
# @param [Pry::WrappedModule] wrapper The associated
-
# `Pry::WrappedModule` instance that owns the candidates.
-
# @param [Fixnum] rank The rank of the candidate to
-
# retrieve. Passing 0 returns 'primary candidate' (the candidate with largest
-
# number of methods), passing 1 retrieves candidate with
-
# second largest number of methods, and so on, up to
-
# `Pry::WrappedModule#number_of_candidates() - 1`
-
1
def initialize(wrapper, rank)
-
@wrapper = wrapper
-
-
if number_of_candidates <= 0
-
raise CommandError, "Cannot find a definition for #{name} module!"
-
elsif rank > (number_of_candidates - 1)
-
raise CommandError, "No such module candidate. Allowed candidates range is from 0 to #{number_of_candidates - 1}"
-
end
-
-
@rank = rank
-
@file, @line = source_location
-
end
-
-
# @raise [Pry::CommandError] If source code cannot be found.
-
# @return [String] The source for the candidate, i.e the
-
# complete module/class definition.
-
1
def source
-
return nil if file.nil?
-
return @source if @source
-
-
@source = strip_leading_whitespace(Pry::Code.from_file(file).expression_at(line, number_of_lines_in_first_chunk))
-
end
-
-
# @raise [Pry::CommandError] If documentation cannot be found.
-
# @return [String] The documentation for the candidate.
-
1
def doc
-
return nil if file.nil?
-
return @doc if @doc
-
-
@doc = get_comment_content(Pry::Code.from_file(file).comment_describing(line))
-
end
-
-
# @return [Array, nil] A `[String, Fixnum]` pair representing the
-
# source location (file and line) for the candidate or `nil`
-
# if no source location found.
-
1
def source_location
-
return @source_location if @source_location
-
-
file, line = first_method_source_location
-
return nil if !file.is_a?(String)
-
-
@source_location = [file, first_line_of_module_definition(file, line)]
-
rescue Pry::RescuableException
-
nil
-
end
-
-
1
private
-
-
# Locate the first line of the module definition.
-
# @param [String] file The file that contains the module
-
# definition (somewhere).
-
# @param [Fixnum] line The module definition should appear
-
# before this line (if it exists).
-
# @return [Fixnum] The line where the module is defined. This
-
# line number is one-indexed.
-
1
def first_line_of_module_definition(file, line)
-
searchable_lines = lines_for_file(file)[0..(line - 2)]
-
searchable_lines.rindex { |v| class_regexes.any? { |r| r =~ v } } + 1
-
end
-
-
1
def class_regexes
-
mod_type_string = wrapped.class.to_s.downcase
-
[/^\s*#{mod_type_string}\s+(?:(?:\w*)::)*?#{wrapped.name.split(/::/).last}/,
-
/^\s*(::)?#{wrapped.name.split(/::/).last}\s*?=\s*?#{wrapped.class}/,
-
/^\s*(::)?#{wrapped.name.split(/::/).last}\.(class|instance)_eval/]
-
end
-
-
# This method is used by `Candidate#source_location` as a
-
# starting point for the search for the candidate's definition.
-
# @return [Array] The source location of the base method used to
-
# calculate the source location of the candidate.
-
1
def first_method_source_location
-
@first_method_source_location ||= method_candidates[@rank].first.source_location
-
end
-
-
# @return [Array] The source location of the last method in this
-
# candidate's module definition.
-
1
def last_method_source_location
-
@end_method_source_location ||= method_candidates[@rank].last.source_location
-
end
-
-
# Return the number of lines between the start of the class definition
-
# and the start of the last method. We use this value so we can
-
# quickly grab these lines from the file (without having to
-
# check each intervening line for validity, which is expensive) speeding up source extraction.
-
# @return [Fixum] Number of lines.
-
1
def number_of_lines_in_first_chunk
-
end_method_line = last_method_source_location.last
-
-
end_method_line - line
-
end
-
end
-
end
-
end
-
1
class Pry
-
# `ObjectPath` implements the resolution of "object paths", which are strings
-
# that are similar to filesystem paths but meant for traversing Ruby objects.
-
# Examples of valid object paths include:
-
#
-
# x
-
# @foo/@bar
-
# "string"/upcase
-
# Pry/Method
-
#
-
# Object paths are mostly relevant in the context of the `cd` command.
-
# @see https://github.com/pry/pry/wiki/State-navigation
-
1
class ObjectPath
-
1
SPECIAL_TERMS = ["", "::", ".", ".."]
-
-
# @param [String] path_string The object path expressed as a string.
-
# @param [Array<Binding>] current_stack The current state of the binding
-
# stack.
-
1
def initialize(path_string, current_stack)
-
@path_string = path_string
-
@current_stack = current_stack
-
end
-
-
# @return [Array<Binding>] a new stack resulting from applying the given
-
# path to the current stack.
-
1
def resolve
-
scanner = StringScanner.new(@path_string.strip)
-
stack = @current_stack.dup
-
-
begin
-
next_segment = ""
-
-
loop do
-
# Scan for as long as we don't see a slash
-
next_segment << scanner.scan(/[^\/]*/)
-
-
if complete?(next_segment) || scanner.eos?
-
scanner.getch # consume the slash
-
break
-
else
-
next_segment << scanner.getch # append the slash
-
end
-
end
-
-
case next_segment.chomp
-
when ""
-
stack = [stack.first]
-
when "::"
-
stack.push(TOPLEVEL_BINDING)
-
when "."
-
next
-
when ".."
-
stack.pop unless stack.size == 1
-
else
-
stack.push(Pry.binding_for(stack.last.eval(next_segment)))
-
end
-
rescue RescuableException => e
-
return handle_failure(next_segment, e)
-
end until scanner.eos?
-
-
stack
-
end
-
-
1
private
-
-
1
def complete?(segment)
-
SPECIAL_TERMS.include?(segment) || Pry::Code.complete_expression?(segment)
-
end
-
-
1
def handle_failure(context, err)
-
msg = [
-
"Bad object path: #{@path_string.inspect}",
-
"Failed trying to resolve: #{context.inspect}",
-
"Exception: #{err.inspect}"
-
].join("\n")
-
-
raise CommandError.new(msg).tap { |e|
-
e.set_backtrace err.backtrace
-
}
-
end
-
end
-
end
-
1
class Pry
-
1
class Output
-
1
attr_reader :_pry_
-
-
1
def initialize(_pry_)
-
75
@_pry_ = _pry_
-
end
-
-
1
def puts(*objs)
-
8
return print "\n" if objs.empty?
-
-
8
objs.each do |obj|
-
8
if ary = Array.try_convert(obj)
-
puts(*ary)
-
else
-
8
print "#{obj.to_s.chomp}\n"
-
end
-
end
-
-
nil
-
end
-
-
1
def print(*objs)
-
8
objs.each do |obj|
-
8
_pry_.config.output.print decolorize_maybe(obj.to_s)
-
end
-
-
nil
-
end
-
1
alias << print
-
1
alias write print
-
-
# If _pry_.config.color is currently false, removes ansi escapes from the string.
-
1
def decolorize_maybe(str)
-
9
if _pry_.config.color
-
9
str
-
else
-
Helpers::Text.strip_color str
-
end
-
end
-
-
1
def method_missing(name, *args, &block)
-
24
_pry_.config.output.send(name, *args, &block)
-
end
-
-
1
def respond_to_missing?(*a)
-
7
_pry_.config.respond_to?(*a)
-
end
-
end
-
end
-
1
require 'pry/terminal'
-
-
# A pager is an `IO`-like object that accepts text and either prints it
-
# immediately, prints it one page at a time, or streams it to an external
-
# program to print one page at a time.
-
1
class Pry::Pager
-
1
class StopPaging < StandardError
-
end
-
-
1
attr_reader :_pry_
-
-
1
def initialize(_pry_)
-
5
@_pry_ = _pry_
-
end
-
-
# Send the given text through the best available pager (if `Pry.config.pager` is
-
# enabled).
-
# If you want to send text through in chunks as you generate it, use `open` to
-
# get a writable object instead.
-
# @param [String] text A piece of text to run through a pager.
-
# @param [IO] output (`$stdout`) An object to send output to.
-
1
def page(text)
-
4
open do |pager|
-
4
pager << text
-
end
-
end
-
-
# Yields a pager object (`NullPager`, `SimplePager`, or `SystemPager`). All
-
# pagers accept output with `#puts`, `#print`, `#write`, and `#<<`.
-
# @param [IO] output (`$stdout`) An object to send output to.
-
1
def open
-
5
pager = best_available
-
5
yield pager
-
rescue StopPaging
-
ensure
-
5
pager.close if pager
-
end
-
-
1
private
-
-
1
def enabled?; !!@enabled; end
-
-
1
def output; @output; end
-
-
# Return an instance of the "best" available pager class -- `SystemPager` if
-
# possible, `SimplePager` if `SystemPager` isn't available, and `NullPager`
-
# if the user has disabled paging. All pagers accept output with `#puts`,
-
# `#print`, `#write`, and `#<<`. You must call `#close` when you're done
-
# writing output to a pager, and you must rescue `Pry::Pager::StopPaging`.
-
# These requirements can be avoided by using `.open` instead.
-
# @param [#<<] output ($stdout) An object to send output to.
-
1
def best_available
-
5
if !_pry_.config.pager
-
NullPager.new(_pry_.output)
-
5
elsif !SystemPager.available? || Pry::Helpers::BaseHelpers.jruby?
-
SimplePager.new(_pry_.output)
-
else
-
5
SystemPager.new(_pry_.output)
-
end
-
end
-
-
# `NullPager` is a "pager" that actually just prints all output as it comes
-
# in. Used when `Pry.config.pager` is false.
-
1
class NullPager
-
1
def initialize(out)
-
5
@out = out
-
end
-
-
1
def puts(str)
-
print "#{str.chomp}\n"
-
end
-
-
1
def print(str)
-
9
write str
-
end
-
1
alias << print
-
-
1
def write(str)
-
@out.write str
-
end
-
-
1
def close
-
end
-
-
1
private
-
-
1
def height
-
5
@height ||= Pry::Terminal.height!
-
end
-
-
1
def width
-
5
@width ||= Pry::Terminal.width!
-
end
-
end
-
-
# `SimplePager` is a straightforward pure-Ruby pager. We use it on JRuby and
-
# when we can't find a usable external pager.
-
1
class SimplePager < NullPager
-
1
def initialize(*)
-
super
-
@tracker = PageTracker.new(height - 3, width)
-
end
-
-
1
def write(str)
-
str.lines.each do |line|
-
@out.print line
-
@tracker.record line
-
-
if @tracker.page?
-
@out.print "\n"
-
@out.print "\e[0m"
-
@out.print "<page break> --- Press enter to continue " \
-
"( q<enter> to break ) --- <page break>\n"
-
raise StopPaging if Readline.readline("").chomp == "q"
-
@tracker.reset
-
end
-
end
-
end
-
end
-
-
# `SystemPager` buffers output until we're pretty sure it's at least a page
-
# long, then invokes an external pager and starts streaming output to it. If
-
# `#close` is called before then, it just prints out the buffered content.
-
1
class SystemPager < NullPager
-
1
def self.default_pager
-
2
pager = ENV["PAGER"] || ""
-
-
# Default to less, and make sure less is being passed the correct options
-
2
if pager.strip.empty? or pager =~ /^less\b/
-
2
pager = "less -R -F -X"
-
end
-
-
2
pager
-
end
-
-
1
def self.available?
-
5
if @system_pager.nil?
-
1
@system_pager = begin
-
1
pager_executable = default_pager.split(' ').first
-
1
`which #{pager_executable}`
-
1
$?.success?
-
rescue
-
false
-
end
-
else
-
4
@system_pager
-
end
-
end
-
-
1
def initialize(*)
-
5
super
-
5
@tracker = PageTracker.new(height, width)
-
5
@buffer = ""
-
end
-
-
1
def write(str)
-
9
if invoked_pager?
-
write_to_pager str
-
else
-
9
@tracker.record str
-
9
@buffer << str
-
-
9
if @tracker.page?
-
1
write_to_pager @buffer
-
end
-
end
-
rescue Errno::EPIPE
-
raise StopPaging
-
end
-
-
1
def close
-
5
if invoked_pager?
-
1
pager.close
-
else
-
4
@out.puts @buffer
-
end
-
end
-
-
1
private
-
-
1
def write_to_pager(text)
-
1
pager.write @out.decolorize_maybe(text)
-
end
-
-
1
def invoked_pager?
-
14
@pager
-
end
-
-
1
def pager
-
2
@pager ||= IO.popen(self.class.default_pager, 'w')
-
end
-
end
-
-
# `PageTracker` tracks output to determine whether it's likely to take up a
-
# whole page. This doesn't need to be super precise, but we can use it for
-
# `SimplePager` and to avoid invoking the system pager unnecessarily.
-
#
-
# One simplifying assumption is that we don't need `#page?` to return `true`
-
# on the basis of an incomplete line. Long lines should be counted as
-
# multiple lines, but we don't have to transition from `false` to `true`
-
# until we see a newline.
-
1
class PageTracker
-
1
def initialize(rows, cols)
-
5
@rows, @cols = rows, cols
-
5
reset
-
end
-
-
1
def record(str)
-
9
str.lines.each do |line|
-
58
if line.end_with? "\n"
-
56
@row += ((@col + line_length(line) - 1) / @cols) + 1
-
56
@col = 0
-
else
-
2
@col += line_length(line)
-
end
-
end
-
end
-
-
1
def page?
-
9
@row >= @rows
-
end
-
-
1
def reset
-
5
@row = 0
-
5
@col = 0
-
end
-
-
1
private
-
-
# Approximation of the printable length of a given line, without the
-
# newline and without ANSI color codes.
-
1
def line_length(line)
-
58
line.chomp.gsub(/\e\[[\d;]*m/, '').length
-
end
-
end
-
end
-
1
class Pry
-
1
class PluginManager
-
1
PRY_PLUGIN_PREFIX = /^pry-/
-
-
# Placeholder when no associated gem found, displays warning
-
1
class NoPlugin
-
1
def initialize(name)
-
@name = name
-
end
-
-
1
def method_missing(*args)
-
warn "Warning: The plugin '#{@name}' was not found! (no gem found)"
-
end
-
end
-
-
1
class Plugin
-
1
attr_accessor :name, :gem_name, :enabled, :spec, :active
-
-
1
def initialize(name, gem_name, spec, enabled)
-
@name, @gem_name, @enabled, @spec = name, gem_name, enabled, spec
-
end
-
-
# Disable a plugin. (prevents plugin from being loaded, cannot
-
# disable an already activated plugin)
-
1
def disable!
-
self.enabled = false
-
end
-
-
# Enable a plugin. (does not load it immediately but puts on
-
# 'white list' to be loaded)
-
1
def enable!
-
self.enabled = true
-
end
-
-
# Load the Command line options defined by this plugin (if they exist)
-
1
def load_cli_options
-
cli_options_file = File.join(spec.full_gem_path, "lib/#{spec.name}/cli.rb")
-
require cli_options_file if File.exist?(cli_options_file)
-
end
-
# Activate the plugin (require the gem - enables/loads the
-
# plugin immediately at point of call, even if plugin is
-
# disabled)
-
# Does not reload plugin if it's already active.
-
1
def activate!
-
# Create the configuration object for the plugin.
-
Pry.config.send("#{gem_name.gsub('-', '_')}=", Pry::Config.from_hash({}))
-
-
begin
-
require gem_name if !active?
-
rescue LoadError => e
-
warn "Found plugin #{gem_name}, but could not require '#{gem_name}'"
-
warn e
-
rescue => e
-
warn "require '#{gem_name}' # Failed, saying: #{e}"
-
end
-
-
self.active = true
-
self.enabled = true
-
end
-
-
1
alias active? active
-
1
alias enabled? enabled
-
end
-
-
1
def initialize
-
1
@plugins = []
-
end
-
-
# Find all installed Pry plugins and store them in an internal array.
-
1
def locate_plugins
-
1
Gem.refresh
-
1
(Gem::Specification.respond_to?(:each) ? Gem::Specification : Gem.source_index.find_name('')).each do |gem|
-
17
next if gem.name !~ PRY_PLUGIN_PREFIX
-
plugin_name = gem.name.split('-', 2).last
-
@plugins << Plugin.new(plugin_name, gem.name, gem, true) if !gem_located?(gem.name)
-
end
-
1
@plugins
-
end
-
-
# @return [Hash] A hash with all plugin names (minus the 'pry-') as
-
# keys and Plugin objects as values.
-
1
def plugins
-
1
h = Hash.new { |_, key| NoPlugin.new(key) }
-
1
@plugins.each do |plugin|
-
h[plugin.name] = plugin
-
end
-
1
h
-
end
-
-
# Require all enabled plugins, disabled plugins are skipped.
-
1
def load_plugins
-
1
@plugins.each do |plugin|
-
plugin.activate! if plugin.enabled?
-
end
-
end
-
-
1
private
-
1
def gem_located?(gem_name)
-
@plugins.any? { |plugin| plugin.gem_name == gem_name }
-
end
-
end
-
-
end
-
1
class Pry::Prompt
-
1
MAP = {
-
"default" => {
-
value: Pry::DEFAULT_PROMPT,
-
description: "The default Pry prompt. Includes information about the\n" \
-
"current expression number, evaluation context, and nesting\n" \
-
"level, plus a reminder that you're using Pry."
-
},
-
-
"simple" => {
-
value: Pry::SIMPLE_PROMPT,
-
description: "A simple '>>'."
-
},
-
-
"nav" => {
-
value: Pry::NAV_PROMPT,
-
description: "A prompt that displays the binding stack as a path and\n" \
-
"includes information about _in_ and _out_."
-
},
-
-
"none" => {
-
value: Pry::NO_PROMPT,
-
description: "Wave goodbye to the Pry prompt."
-
}
-
}
-
end
-
1
require 'pry/config'
-
1
class Pry
-
-
1
HOME_RC_FILE = ENV["PRYRC"] || "~/.pryrc"
-
1
LOCAL_RC_FILE = "./.pryrc"
-
-
1
class << self
-
1
extend Forwardable
-
1
attr_accessor :custom_completions
-
1
attr_accessor :current_line
-
1
attr_accessor :line_buffer
-
1
attr_accessor :eval_path
-
1
attr_accessor :cli
-
1
attr_accessor :quiet
-
1
attr_accessor :last_internal_error
-
1
attr_accessor :config
-
1
attr_writer :history
-
-
1
def_delegators :@plugin_manager, :plugins, :load_plugins, :locate_plugins
-
-
1
extend Pry::Config::Convenience
-
1
config_shortcut(*Pry::Config.shortcuts)
-
-
1
def prompt=(value)
-
config.prompt = value
-
end
-
-
1
def prompt
-
config.prompt
-
end
-
-
1
def history
-
73
@history ||= History.new
-
end
-
end
-
-
#
-
# @return [main]
-
# returns the special instance of Object, "main".
-
#
-
1
def self.main
-
24
@main ||= TOPLEVEL_BINDING.eval "self"
-
end
-
-
#
-
# @return [Pry::Config]
-
# Returns a value store for an instance of Pry running on the current thread.
-
#
-
1
def self.current
-
24
Thread.current[:__pry__] ||= Pry::Config.from_hash({}, nil)
-
end
-
-
# Load the given file in the context of `Pry.toplevel_binding`
-
# @param [String] file The unexpanded file path.
-
1
def self.load_file_at_toplevel(file)
-
toplevel_binding.eval(File.read(file), file)
-
rescue RescuableException => e
-
puts "Error loading #{file}: #{e}\n#{e.backtrace.first}"
-
end
-
-
# Load HOME_RC_FILE and LOCAL_RC_FILE if appropriate
-
# This method can also be used to reload the files if they have changed.
-
1
def self.load_rc_files
-
1
rc_files_to_load.each do |file|
-
critical_section do
-
load_file_at_toplevel(file)
-
end
-
end
-
end
-
-
# Load the local RC file (./.pryrc)
-
1
def self.rc_files_to_load
-
1
files = []
-
1
files << HOME_RC_FILE if Pry.config.should_load_rc
-
1
files << LOCAL_RC_FILE if Pry.config.should_load_local_rc
-
3
files.map { |file| real_path_to(file) }.compact.uniq
-
end
-
-
# Expand a file to its canonical name (following symlinks as appropriate)
-
1
def self.real_path_to(file)
-
2
expanded = Pathname.new(File.expand_path(file)).realpath.to_s
-
# For rbx 1.9 mode [see rubinius issue #2165]
-
File.exist?(expanded) ? expanded : nil
-
rescue Errno::ENOENT
-
2
nil
-
end
-
-
# Load any Ruby files specified with the -r flag on the command line.
-
1
def self.load_requires
-
1
Pry.config.requires.each do |file|
-
require file
-
end
-
end
-
-
# Trap interrupts on jruby, and make them behave like MRI so we can
-
# catch them.
-
1
def self.load_traps
-
trap('INT'){ raise Interrupt }
-
end
-
-
1
def self.load_win32console
-
begin
-
require 'win32console'
-
# The mswin and mingw versions of pry require win32console, so this should
-
# only fail on jruby (where win32console doesn't work).
-
# Instead we'll recommend ansicon, which does.
-
rescue LoadError
-
warn <<-WARNING if Pry.config.windows_console_warning
-
For a better Pry experience on Windows, please use ansicon:
-
https://github.com/adoxa/ansicon
-
If you use an alternative to ansicon and don't want to see this warning again,
-
you can add "Pry.config.windows_console_warning = false" to your .pryrc.
-
WARNING
-
end
-
end
-
-
# Do basic setup for initial session.
-
# Including: loading .pryrc, loading plugins, loading requires, and
-
# loading history.
-
1
def self.initial_session_setup
-
4
return unless initial_session?
-
1
@initial_session = false
-
-
# note these have to be loaded here rather than in pry_instance as
-
# we only want them loaded once per entire Pry lifetime.
-
1
load_rc_files
-
1
load_plugins if Pry.config.should_load_plugins
-
1
load_requires if Pry.config.should_load_requires
-
1
load_history if Pry.config.history.should_load
-
1
load_traps if Pry.config.should_trap_interrupts
-
1
load_win32console if Pry::Helpers::BaseHelpers.windows? && !Pry::Helpers::BaseHelpers.windows_ansi?
-
end
-
-
# Start a Pry REPL.
-
# This method also loads `~/.pryrc` and `./.pryrc` as necessary the
-
# first time it is invoked.
-
# @param [Object, Binding] target The receiver of the Pry session
-
# @param [Hash] options
-
# @option options (see Pry#initialize)
-
# @example
-
# Pry.start(Object.new, :input => MyInput.new)
-
1
def self.start(target=nil, options={})
-
4
return if ENV['DISABLE_PRY']
-
4
options = options.to_hash
-
-
4
if in_critical_section?
-
output.puts "ERROR: Pry started inside Pry."
-
output.puts "This can happen if you have a binding.pry inside a #to_s or #inspect function."
-
return
-
end
-
-
4
options[:target] = Pry.binding_for(target || toplevel_binding)
-
4
options[:hooks] = Pry::Hooks.from_hash options.delete(:hooks) if options.key?(:hooks)
-
4
initial_session_setup
-
-
# Unless we were given a backtrace, save the current one
-
4
if options[:backtrace].nil?
-
4
options[:backtrace] = caller
-
-
# If Pry was started via `binding.pry`, elide that from the backtrace
-
4
if options[:backtrace].first =~ /pry.*core_extensions.*pry/
-
4
options[:backtrace].shift
-
end
-
end
-
-
4
driver = options[:driver] || Pry::REPL
-
-
# Enter the matrix
-
4
driver.start(options)
-
rescue Pry::TooSafeException
-
puts "ERROR: Pry cannot work with $SAFE > 0"
-
raise
-
end
-
-
# Execute the file through the REPL loop, non-interactively.
-
# @param [String] file_name File name to load through the REPL.
-
1
def self.load_file_through_repl(file_name)
-
require "pry/repl_file_loader"
-
REPLFileLoader.new(file_name).load
-
end
-
-
#
-
# An inspector that clips the output to `max_length` chars.
-
# In case of > `max_length` chars the `#<Object...> notation is used.
-
#
-
# @param [Object] obj
-
# The object to view.
-
#
-
# @param [Hash] options
-
# @option options [Integer] :max_length (60)
-
# The maximum number of chars before clipping occurs.
-
#
-
# @option options [Boolean] :id (false)
-
# Boolean to indicate whether or not a hex reprsentation of the object ID
-
# is attached to the return value when the length of inspect is greater than
-
# value of `:max_length`.
-
#
-
# @return [String]
-
# The string representation of `obj`.
-
#
-
1
def self.view_clip(obj, options = {})
-
24
max = options.fetch :max_length, 60
-
24
id = options.fetch :id, false
-
24
if obj.kind_of?(Module) && obj.name.to_s != "" && obj.name.to_s.length <= max
-
obj.name.to_s
-
24
elsif Pry.main == obj
-
# special-case to support jruby.
-
# fixed as of https://github.com/jruby/jruby/commit/d365ebd309cf9df3dde28f5eb36ea97056e0c039
-
# we can drop in the future.
-
obj.to_s
-
168
elsif Pry.config.prompt_safe_objects.any? { |v| v === obj } && obj.inspect.length <= max
-
obj.inspect
-
else
-
24
id == true ? "#<#{obj.class}:0x%x>" % (obj.object_id << 1) : "#<#{obj.class}>"
-
end
-
rescue RescuableException
-
"unknown"
-
end
-
-
# Load Readline history if required.
-
1
def self.load_history
-
1
Pry.history.load
-
end
-
-
# @return [Boolean] Whether this is the first time a Pry session has
-
# been started since loading the Pry class.
-
1
def self.initial_session?
-
4
@initial_session
-
end
-
-
# Run a Pry command from outside a session. The commands available are
-
# those referenced by `Pry.config.commands` (the default command set).
-
# @param [String] command_string The Pry command (including arguments,
-
# if any).
-
# @param [Hash] options Optional named parameters.
-
# @return [Object] The return value of the Pry command.
-
# @option options [Object, Binding] :target The object to run the
-
# command under. Defaults to `TOPLEVEL_BINDING` (main).
-
# @option options [Boolean] :show_output Whether to show command
-
# output. Defaults to true.
-
# @example Run at top-level with no output.
-
# Pry.run_command "ls"
-
# @example Run under Pry class, returning only public methods.
-
# Pry.run_command "ls -m", :target => Pry
-
# @example Display command output.
-
# Pry.run_command "ls -av", :show_output => true
-
1
def self.run_command(command_string, options={})
-
options = {
-
:target => TOPLEVEL_BINDING,
-
:show_output => true,
-
:output => Pry.config.output,
-
:commands => Pry.config.commands
-
}.merge!(options)
-
-
# :context for compatibility with <= 0.9.11.4
-
target = options[:context] || options[:target]
-
output = options[:show_output] ? options[:output] : StringIO.new
-
-
pry = Pry.new(:output => output, :target => target, :commands => options[:commands])
-
pry.eval command_string
-
end
-
-
1
def self.default_editor_for_platform
-
return ENV['VISUAL'] if ENV['VISUAL'] and not ENV['VISUAL'].empty?
-
return ENV['EDITOR'] if ENV['EDITOR'] and not ENV['EDITOR'].empty?
-
if Helpers::BaseHelpers.windows?
-
'notepad'
-
else
-
%w(editor nano vi).detect do |editor|
-
system("which #{editor} > /dev/null 2>&1")
-
end
-
end
-
end
-
-
1
def self.auto_resize!
-
Pry.config.input # by default, load Readline
-
-
if !defined?(Readline) || Pry.config.input != Readline
-
warn "Sorry, you must be using Readline for Pry.auto_resize! to work."
-
return
-
end
-
-
if Readline::VERSION =~ /edit/i
-
warn <<-EOT
-
Readline version #{Readline::VERSION} detected - will not auto_resize! correctly.
-
For the fix, use GNU Readline instead:
-
https://github.com/guard/guard/wiki/Add-proper-Readline-support-to-Ruby-on-Mac-OS-X
-
EOT
-
return
-
end
-
-
trap :WINCH do
-
begin
-
Readline.set_screen_size(*Terminal.size!)
-
rescue => e
-
warn "\nPry.auto_resize!'s Readline.set_screen_size failed: #{e}"
-
end
-
begin
-
Readline.refresh_line
-
rescue => e
-
warn "\nPry.auto_resize!'s Readline.refresh_line failed: #{e}"
-
end
-
end
-
end
-
-
# Set all the configurable options back to their default values
-
1
def self.reset_defaults
-
1
@initial_session = true
-
1
self.config = Pry::Config.new Pry::Config::Default.new
-
1
self.cli = false
-
1
self.current_line = 1
-
1
self.line_buffer = [""]
-
1
self.eval_path = "(pry)"
-
end
-
-
# Basic initialization.
-
1
def self.init
-
1
@plugin_manager ||= PluginManager.new
-
1
reset_defaults
-
1
locate_plugins
-
end
-
-
# Return a `Binding` object for `target` or return `target` if it is
-
# already a `Binding`.
-
# In the case where `target` is top-level then return `TOPLEVEL_BINDING`
-
# @param [Object] target The object to get a `Binding` object for.
-
# @return [Binding] The `Binding` object.
-
1
def self.binding_for(target)
-
8
if Binding === target
-
8
target
-
else
-
if Pry.main == target
-
TOPLEVEL_BINDING
-
else
-
target.__binding__
-
end
-
end
-
end
-
-
1
def self.toplevel_binding
-
unless defined?(@toplevel_binding) && @toplevel_binding
-
# Grab a copy of the TOPLEVEL_BINDING without any local variables.
-
# This binding has a default definee of Object, and new methods are
-
# private (just as in TOPLEVEL_BINDING).
-
TOPLEVEL_BINDING.eval <<-RUBY
-
def self.__pry__
-
binding
-
end
-
Pry.toplevel_binding = __pry__
-
class << self; undef __pry__; end
-
RUBY
-
end
-
@toplevel_binding.eval('private')
-
@toplevel_binding
-
end
-
-
1
def self.toplevel_binding=(binding)
-
@toplevel_binding = binding
-
end
-
-
1
def self.in_critical_section?
-
4
Thread.current[:pry_critical_section] ||= 0
-
4
Thread.current[:pry_critical_section] > 0
-
end
-
-
1
def self.critical_section(&block)
-
31
Thread.current[:pry_critical_section] ||= 0
-
31
Thread.current[:pry_critical_section] += 1
-
31
yield
-
ensure
-
31
Thread.current[:pry_critical_section] -= 1
-
end
-
end
-
-
1
Pry.init
-
# -*- coding: utf-8 -*-
-
##
-
# Pry is a powerful alternative to the standard IRB shell for Ruby. It
-
# features syntax highlighting, a flexible plugin architecture, runtime
-
# invocation and source and documentation browsing.
-
#
-
# Pry can be started similar to other command line utilities by simply running
-
# the following command:
-
#
-
# pry
-
#
-
# Once inside Pry you can invoke the help message:
-
#
-
# help
-
#
-
# This will show a list of available commands and their usage. For more
-
# information about Pry you can refer to the following resources:
-
#
-
# * http://pry.github.com/
-
# * https://github.com/pry/pry
-
# * the IRC channel, which is #pry on the Freenode network
-
#
-
-
1
class Pry
-
1
attr_accessor :binding_stack
-
1
attr_accessor :custom_completions
-
1
attr_accessor :eval_string
-
1
attr_accessor :backtrace
-
1
attr_accessor :suppress_output
-
1
attr_accessor :last_result
-
1
attr_accessor :last_file
-
1
attr_accessor :last_dir
-
-
1
attr_reader :last_exception
-
1
attr_reader :command_state
-
1
attr_reader :exit_value
-
1
attr_reader :input_array
-
1
attr_reader :output_array
-
1
attr_reader :config
-
-
1
extend Pry::Config::Convenience
-
1
config_shortcut(*Pry::Config.shortcuts)
-
1
EMPTY_COMPLETIONS = [].freeze
-
-
# Create a new {Pry} instance.
-
# @param [Hash] options
-
# @option options [#readline] :input
-
# The object to use for input.
-
# @option options [#puts] :output
-
# The object to use for output.
-
# @option options [Pry::CommandBase] :commands
-
# The object to use for commands.
-
# @option options [Hash] :hooks
-
# The defined hook Procs.
-
# @option options [Array<Proc>] :prompt
-
# The array of Procs to use for prompts.
-
# @option options [Proc] :print
-
# The Proc to use for printing return values.
-
# @option options [Boolean] :quiet
-
# Omit the `whereami` banner when starting.
-
# @option options [Array<String>] :backtrace
-
# The backtrace of the session's `binding.pry` line, if applicable.
-
# @option options [Object] :target
-
# The initial context for this session.
-
1
def initialize(options={})
-
4
@binding_stack = []
-
4
@indent = Pry::Indent.new
-
4
@command_state = {}
-
4
@eval_string = ""
-
4
@backtrace = options.delete(:backtrace) || caller
-
4
target = options.delete(:target)
-
4
@config = Pry::Config.new
-
4
config.merge!(options)
-
4
push_prompt(config.prompt)
-
4
@input_array = Pry::HistoryArray.new config.memory_size
-
4
@output_array = Pry::HistoryArray.new config.memory_size
-
4
@custom_completions = config.command_completions
-
4
set_last_result nil
-
4
@input_array << nil
-
4
push_initial_binding(target)
-
4
exec_hook(:when_started, target, options, self)
-
end
-
-
# The current prompt.
-
# This is the prompt at the top of the prompt stack.
-
#
-
# @example
-
# self.prompt = Pry::SIMPLE_PROMPT
-
# self.prompt # => Pry::SIMPLE_PROMPT
-
#
-
# @return [Array<Proc>] Current prompt.
-
1
def prompt
-
24
prompt_stack.last
-
end
-
-
1
def prompt=(new_prompt)
-
if prompt_stack.empty?
-
push_prompt new_prompt
-
else
-
prompt_stack[-1] = new_prompt
-
end
-
end
-
-
# Initialize this instance by pushing its initial context into the binding
-
# stack. If no target is given, start at the top level.
-
1
def push_initial_binding(target=nil)
-
4
push_binding(target || Pry.toplevel_binding)
-
end
-
-
# The currently active `Binding`.
-
# @return [Binding] The currently active `Binding` for the session.
-
1
def current_binding
-
363
binding_stack.last
-
end
-
1
alias current_context current_binding # support previous API
-
-
# Push a binding for the given object onto the stack. If this instance is
-
# currently stopped, mark it as usable again.
-
1
def push_binding(object)
-
4
@stopped = false
-
4
binding_stack << Pry.binding_for(object)
-
end
-
-
#
-
# Generate completions.
-
#
-
# @param [String] input
-
# What the user has typed so far
-
#
-
# @return [Array<String>]
-
# Possible completions
-
#
-
1
def complete(str)
-
return EMPTY_COMPLETIONS unless config.completer
-
Pry.critical_section do
-
completer = config.completer.new(config.input, self)
-
completer.call str, target: current_binding, custom_completions: custom_completions.call.push(*sticky_locals.keys)
-
end
-
end
-
-
#
-
# Injects a local variable into the provided binding.
-
#
-
# @param [String] name
-
# The name of the local to inject.
-
#
-
# @param [Object] value
-
# The value to set the local to.
-
#
-
# @param [Binding] b
-
# The binding to set the local on.
-
#
-
# @return [Object]
-
# The value the local was set to.
-
#
-
1
def inject_local(name, value, b)
-
280
value = Proc === value ? value.call : value
-
280
if b.respond_to?(:local_variable_set)
-
280
b.local_variable_set name, value
-
else # < 2.1
-
begin
-
Pry.current[:pry_local] = value
-
b.eval "#{name} = ::Pry.current[:pry_local]"
-
ensure
-
Pry.current[:pry_local] = nil
-
end
-
end
-
end
-
-
1
undef :memory_size if method_defined? :memory_size
-
# @return [Integer] The maximum amount of objects remembered by the inp and
-
# out arrays. Defaults to 100.
-
1
def memory_size
-
@output_array.max_size
-
end
-
-
1
undef :memory_size= if method_defined? :memory_size=
-
1
def memory_size=(size)
-
@input_array = Pry::HistoryArray.new(size)
-
@output_array = Pry::HistoryArray.new(size)
-
end
-
-
# Inject all the sticky locals into the current binding.
-
1
def inject_sticky_locals!
-
34
sticky_locals.each_pair do |name, value|
-
272
inject_local(name, value, current_binding)
-
end
-
end
-
-
# Add a sticky local to this Pry instance.
-
# A sticky local is a local that persists between all bindings in a session.
-
# @param [Symbol] name The name of the sticky local.
-
# @yield The block that defines the content of the local. The local
-
# will be refreshed at each tick of the repl loop.
-
1
def add_sticky_local(name, &block)
-
config.extra_sticky_locals[name] = block
-
end
-
-
1
def sticky_locals
-
{ _in_: input_array,
-
_out_: output_array,
-
_pry_: self,
-
_ex_: last_exception && last_exception.wrapped_exception,
-
_file_: last_file,
-
_dir_: last_dir,
-
34
_: proc { last_result },
-
34
__: proc { output_array[-2] }
-
34
}.merge(config.extra_sticky_locals)
-
end
-
-
# Reset the current eval string. If the user has entered part of a multiline
-
# expression, this discards that input.
-
1
def reset_eval_string
-
10
@eval_string = ""
-
end
-
-
# Pass a line of input to Pry.
-
#
-
# This is the equivalent of `Binding#eval` but with extra Pry!
-
#
-
# In particular:
-
# 1. Pry commands will be executed immediately if the line matches.
-
# 2. Partial lines of input will be queued up until a complete expression has
-
# been accepted.
-
# 3. Output is written to `#output` in pretty colours, not returned.
-
#
-
# Once this method has raised an exception or returned false, this instance
-
# is no longer usable. {#exit_value} will return the session's breakout
-
# value if applicable.
-
#
-
# @param [String?] line The line of input; `nil` if the user types `<Ctrl-D>`
-
# @option options [Boolean] :generated Whether this line was generated automatically.
-
# Generated lines are not stored in history.
-
# @return [Boolean] Is Pry ready to accept more input?
-
# @raise [Exception] If the user uses the `raise-up` command, this method
-
# will raise that exception.
-
1
def eval(line, options={})
-
24
return false if @stopped
-
-
24
exit_value = nil
-
24
exception = catch(:raise_up) do
-
24
exit_value = catch(:breakout) do
-
24
handle_line(line, options)
-
# We use 'return !@stopped' here instead of 'return true' so that if
-
# handle_line has stopped this pry instance (e.g. by opening _pry_.repl and
-
# then popping all the bindings) we still exit immediately.
-
21
return !@stopped
-
end
-
exception = false
-
end
-
-
@stopped = true
-
@exit_value = exit_value
-
-
# TODO: make this configurable?
-
raise exception if exception
-
return false
-
end
-
-
1
def handle_line(line, options)
-
24
if line.nil?
-
config.control_d_handler.call(@eval_string, self)
-
return
-
end
-
-
24
ensure_correct_encoding!(line)
-
24
Pry.history << line unless options[:generated]
-
-
24
@suppress_output = false
-
24
inject_sticky_locals!
-
24
begin
-
24
if !process_command_safely(line.lstrip)
-
24
@eval_string << "#{line.chomp}\n" if !line.empty? || !@eval_string.empty?
-
end
-
rescue RescuableException => e
-
self.last_exception = e
-
result = e
-
-
Pry.critical_section do
-
show_result(result)
-
end
-
return
-
end
-
-
# This hook is supposed to be executed after each line of ruby code
-
# has been read (regardless of whether eval_string is yet a complete expression)
-
24
exec_hook :after_read, eval_string, self
-
-
24
begin
-
24
complete_expr = Pry::Code.complete_expression?(@eval_string)
-
rescue SyntaxError => e
-
output.puts "SyntaxError: #{e.message.sub(/.*syntax error, */m, '')}"
-
reset_eval_string
-
end
-
-
24
if complete_expr
-
10
if @eval_string =~ /;\Z/ || @eval_string.empty? || @eval_string =~ /\A *#.*\n\z/
-
4
@suppress_output = true
-
end
-
-
# A bug in jruby makes java.lang.Exception not rescued by
-
# `rescue Pry::RescuableException` clause.
-
#
-
# * https://github.com/pry/pry/issues/854
-
# * https://jira.codehaus.org/browse/JRUBY-7100
-
#
-
# Until that gets fixed upstream, treat java.lang.Exception
-
# as an additional exception to be rescued explicitly.
-
#
-
# This workaround has a side effect: java exceptions specified
-
# in `Pry.config.exception_whitelist` are ignored.
-
10
jruby_exceptions = []
-
10
if Pry::Helpers::BaseHelpers.jruby?
-
jruby_exceptions << Java::JavaLang::Exception
-
end
-
-
10
begin
-
# Reset eval string, in case we're evaluating Ruby that does something
-
# like open a nested REPL on this instance.
-
10
eval_string = @eval_string
-
10
reset_eval_string
-
-
10
result = evaluate_ruby(eval_string)
-
rescue RescuableException, *jruby_exceptions => e
-
# Eliminate following warning:
-
# warning: singleton on non-persistent Java type X
-
# (http://wiki.jruby.org/Persistence)
-
2
if Pry::Helpers::BaseHelpers.jruby? && e.class.respond_to?('__persistent__')
-
e.class.__persistent__ = true
-
end
-
2
self.last_exception = e
-
2
result = e
-
end
-
-
7
Pry.critical_section do
-
7
show_result(result)
-
end
-
end
-
-
21
throw(:breakout) if current_binding.nil?
-
end
-
1
private :handle_line
-
-
# Potentially deprecated — Use `Pry::REPL.new(pry, :target => target).start`
-
# (If nested sessions are going to exist, this method is fine, but a goal is
-
# to come up with an alternative to nested sessions altogether.)
-
1
def repl(target = nil)
-
Pry::REPL.new(self, :target => target).start
-
end
-
-
1
def evaluate_ruby(code)
-
10
inject_sticky_locals!
-
10
exec_hook :before_eval, code, self
-
-
10
result = current_binding.eval(code, Pry.eval_path, Pry.current_line)
-
5
set_last_result(result, code)
-
ensure
-
10
update_input_history(code)
-
10
exec_hook :after_eval, result, self
-
end
-
-
# Output the result or pass to an exception handler (if result is an exception).
-
1
def show_result(result)
-
7
if last_result_is_exception?
-
2
exception_handler.call(output, result, self)
-
5
elsif should_print?
-
1
print.call(output, result, self)
-
else
-
# nothin'
-
end
-
rescue RescuableException => e
-
# Being uber-paranoid here, given that this exception arose because we couldn't
-
# serialize something in the user's program, let's not assume we can serialize
-
# the exception either.
-
begin
-
output.puts "(pry) output error: #{e.inspect}"
-
rescue RescuableException => e
-
if last_result_is_exception?
-
output.puts "(pry) output error: failed to show exception"
-
else
-
output.puts "(pry) output error: failed to show result"
-
end
-
end
-
ensure
-
7
output.flush if output.respond_to?(:flush)
-
end
-
-
# Force `eval_string` into the encoding of `val`. [Issue #284]
-
1
def ensure_correct_encoding!(val)
-
if @eval_string.empty? &&
-
24
val.respond_to?(:encoding) &&
-
val.encoding != @eval_string.encoding
-
@eval_string.force_encoding(val.encoding)
-
end
-
end
-
1
private :ensure_correct_encoding!
-
-
# If the given line is a valid command, process it in the context of the
-
# current `eval_string` and binding.
-
# @param [String] val The line to process.
-
# @return [Boolean] `true` if `val` is a command, `false` otherwise
-
1
def process_command(val)
-
24
val = val.chomp
-
24
result = commands.process_line(val,
-
:target => current_binding,
-
:output => output,
-
:eval_string => @eval_string,
-
:pry_instance => self
-
)
-
-
# set a temporary (just so we can inject the value we want into eval_string)
-
24
Pry.current[:pry_cmd_result] = result
-
-
# note that `result` wraps the result of command processing; if a
-
# command was matched and invoked then `result.command?` returns true,
-
# otherwise it returns false.
-
24
if result.command?
-
if !result.void_command?
-
# the command that was invoked was non-void (had a return value) and so we make
-
# the value of the current expression equal to the return value
-
# of the command.
-
@eval_string.replace "::Pry.current[:pry_cmd_result].retval\n"
-
end
-
true
-
else
-
24
false
-
end
-
end
-
-
# Same as process_command, but outputs exceptions to `#output` instead of
-
# raising.
-
# @param [String] val The line to process.
-
# @return [Boolean] `true` if `val` is a command, `false` otherwise
-
1
def process_command_safely(val)
-
24
process_command(val)
-
rescue CommandError, Slop::InvalidOptionError, MethodSource::SourceNotFoundError => e
-
Pry.last_internal_error = e
-
output.puts "Error: #{e.message}"
-
true
-
end
-
-
# Run the specified command.
-
# @param [String] val The command (and its params) to execute.
-
# @return [Pry::Command::VOID_VALUE]
-
# @example
-
# pry_instance.run_command("ls -m")
-
1
def run_command(val)
-
4
commands.process_line(val,
-
:eval_string => @eval_string,
-
:target => current_binding,
-
:pry_instance => self,
-
:output => output
-
)
-
3
Pry::Command::VOID_VALUE
-
end
-
-
# Execute the specified hook.
-
# @param [Symbol] name The hook name to execute
-
# @param [*Object] args The arguments to pass to the hook
-
# @return [Object, Exception] The return value of the hook or the exception raised
-
#
-
# If executing a hook raises an exception, we log that and then continue sucessfully.
-
# To debug such errors, use the global variable $pry_hook_error, which is set as a
-
# result.
-
1
def exec_hook(name, *args, &block)
-
56
e_before = hooks.errors.size
-
56
hooks.exec_hook(name, *args, &block).tap do
-
55
hooks.errors[e_before..-1].each do |e|
-
output.puts "#{name} hook failed: #{e.class}: #{e.message}"
-
output.puts "#{e.backtrace.first}"
-
output.puts "(see _pry_.hooks.errors to debug)"
-
end
-
end
-
end
-
-
# Set the last result of an eval.
-
# This method should not need to be invoked directly.
-
# @param [Object] result The result.
-
# @param [String] code The code that was run.
-
1
def set_last_result(result, code="")
-
9
@last_result_is_exception = false
-
9
@output_array << result
-
-
9
self.last_result = result unless code =~ /\A\s*\z/
-
end
-
-
#
-
# Set the last exception for a session.
-
#
-
# @param [Exception] e
-
# the last exception.
-
#
-
1
def last_exception=(e)
-
2
last_exception = Pry::LastException.new(e)
-
2
@last_result_is_exception = true
-
2
@output_array << last_exception
-
2
@last_exception = last_exception
-
end
-
-
# Update Pry's internal state after evalling code.
-
# This method should not need to be invoked directly.
-
# @param [String] code The code we just eval'd
-
1
def update_input_history(code)
-
# Always push to the @input_array as the @output_array is always pushed to.
-
10
@input_array << code
-
10
if code
-
10
Pry.line_buffer.push(*code.each_line)
-
10
Pry.current_line += code.lines.count
-
end
-
end
-
-
# @return [Boolean] True if the last result is an exception that was raised,
-
# as opposed to simply an instance of Exception (like the result of
-
# Exception.new)
-
1
def last_result_is_exception?
-
7
@last_result_is_exception
-
end
-
-
# Whether the print proc should be invoked.
-
# Currently only invoked if the output is not suppressed.
-
# @return [Boolean] Whether the print proc should be invoked.
-
1
def should_print?
-
5
!@suppress_output
-
end
-
-
# Returns the appropriate prompt to use.
-
# @return [String] The prompt.
-
1
def select_prompt
-
24
object = current_binding.eval('self')
-
-
24
open_token = @indent.open_delimiters.any? ? @indent.open_delimiters.last :
-
@indent.stack.last
-
-
24
c = Pry::Config.from_hash({
-
:object => object,
-
:nesting_level => binding_stack.size - 1,
-
:open_token => open_token,
-
:session_line => Pry.history.session_line_count + 1,
-
:history_line => Pry.history.history_line_count + 1,
-
:expr_number => input_array.count,
-
:_pry_ => self,
-
:binding_stack => binding_stack,
-
:input_array => input_array,
-
:eval_string => @eval_string,
-
:cont => !@eval_string.empty?})
-
-
24
Pry.critical_section do
-
# If input buffer is empty then use normal prompt
-
24
if eval_string.empty?
-
10
generate_prompt(Array(prompt).first, c)
-
-
# Otherwise use the wait prompt (indicating multi-line expression)
-
else
-
14
generate_prompt(Array(prompt).last, c)
-
end
-
end
-
end
-
-
1
def generate_prompt(prompt_proc, conf)
-
24
if prompt_proc.arity == 1
-
prompt_proc.call(conf)
-
else
-
24
prompt_proc.call(conf.object, conf.nesting_level, conf._pry_)
-
end
-
end
-
1
private :generate_prompt
-
-
# the array that the prompt stack is stored in
-
1
def prompt_stack
-
28
@prompt_stack ||= Array.new
-
end
-
1
private :prompt_stack
-
-
# Pushes the current prompt onto a stack that it can be restored from later.
-
# Use this if you wish to temporarily change the prompt.
-
# @param [Array<Proc>] new_prompt
-
# @return [Array<Proc>] new_prompt
-
# @example
-
# new_prompt = [ proc { '>' }, proc { '>>' } ]
-
# push_prompt(new_prompt) # => new_prompt
-
1
def push_prompt(new_prompt)
-
4
prompt_stack.push new_prompt
-
end
-
-
# Pops the current prompt off of the prompt stack.
-
# If the prompt you are popping is the last prompt, it will not be popped.
-
# Use this to restore the previous prompt.
-
# @return [Array<Proc>] Prompt being popped.
-
# @example
-
# prompt1 = [ proc { '>' }, proc { '>>' } ]
-
# prompt2 = [ proc { '$' }, proc { '>' } ]
-
# pry = Pry.new :prompt => prompt1
-
# pry.push_prompt(prompt2)
-
# pry.pop_prompt # => prompt2
-
# pry.pop_prompt # => prompt1
-
# pry.pop_prompt # => prompt1
-
1
def pop_prompt
-
prompt_stack.size > 1 ? prompt_stack.pop : prompt
-
end
-
-
1
undef :pager if method_defined? :pager
-
# Returns the currently configured pager
-
# @example
-
# _pry_.pager.page text
-
1
def pager
-
5
Pry::Pager.new(self)
-
end
-
-
1
undef :output if method_defined? :output
-
# Returns an output device
-
# @example
-
# _pry_.output.puts "ohai!"
-
1
def output
-
75
Pry::Output.new(self)
-
end
-
-
# Raise an exception out of Pry.
-
#
-
# See Kernel#raise for documentation of parameters.
-
# See rb_make_exception for the inbuilt implementation.
-
#
-
# This is necessary so that the raise-up command can tell the
-
# difference between an exception the user has decided to raise,
-
# and a mistake in specifying that exception.
-
#
-
# (i.e. raise-up RunThymeError.new should not be the same as
-
# raise-up NameError, "unititialized constant RunThymeError")
-
#
-
1
def raise_up_common(force, *args)
-
exception = if args == []
-
last_exception || RuntimeError.new
-
elsif args.length == 1 && args.first.is_a?(String)
-
RuntimeError.new(args.first)
-
elsif args.length > 3
-
raise ArgumentError, "wrong number of arguments"
-
elsif !args.first.respond_to?(:exception)
-
raise TypeError, "exception class/object expected"
-
elsif args.length === 1
-
args.first.exception
-
else
-
args.first.exception(args[1])
-
end
-
-
raise TypeError, "exception object expected" unless exception.is_a? Exception
-
-
exception.set_backtrace(args.length === 3 ? args[2] : caller(1))
-
-
if force || binding_stack.one?
-
binding_stack.clear
-
throw :raise_up, exception
-
else
-
binding_stack.pop
-
raise exception
-
end
-
end
-
1
def raise_up(*args); raise_up_common(false, *args); end
-
1
def raise_up!(*args); raise_up_common(true, *args); end
-
-
# Convenience accessor for the `quiet` config key.
-
# @return [Boolean]
-
1
def quiet?
-
4
config.quiet
-
end
-
end
-
1
class Pry
-
1
module RbxPath
-
1
module_function
-
1
def is_core_path?(path)
-
3
Pry::Helpers::BaseHelpers.rbx? && (path.start_with?("kernel") || path.start_with?("lib")) && File.exist?(convert_path_to_full(path))
-
end
-
-
1
def convert_path_to_full(path)
-
if path.start_with?("kernel")
-
File.join File.dirname(Rubinius::KERNEL_PATH), path
-
elsif path.start_with?("lib")
-
File.join File.dirname(Rubinius::LIB_PATH), path
-
else
-
path
-
end
-
end
-
-
1
def rvm_ruby?(path)
-
!!(path =~ /\.rvm/)
-
end
-
end
-
end
-
1
require 'forwardable'
-
-
1
class Pry
-
1
class REPL
-
1
extend Forwardable
-
1
def_delegators :@pry, :input, :output
-
-
# @return [Pry] The instance of {Pry} that the user is controlling.
-
1
attr_accessor :pry
-
-
# Instantiate a new {Pry} instance with the given options, then start a
-
# {REPL} instance wrapping it.
-
# @option options See {Pry#initialize}
-
1
def self.start(options)
-
4
new(Pry.new(options)).start
-
end
-
-
# Create an instance of {REPL} wrapping the given {Pry}.
-
# @param [Pry] pry The instance of {Pry} that this {REPL} will control.
-
# @param [Hash] options Options for this {REPL} instance.
-
# @option options [Object] :target The initial target of the session.
-
1
def initialize(pry, options = {})
-
4
@pry = pry
-
4
@indent = Pry::Indent.new
-
-
4
if options[:target]
-
@pry.push_binding options[:target]
-
end
-
end
-
-
# Start the read-eval-print loop.
-
# @return [Object?] If the session throws `:breakout`, return the value
-
# thrown with it.
-
# @raise [Exception] If the session throws `:raise_up`, raise the exception
-
# thrown with it.
-
1
def start
-
4
prologue
-
6
Pry::InputLock.for(:all).with_ownership { repl }
-
ensure
-
4
epilogue
-
end
-
-
1
private
-
-
# Set up the repl session.
-
# @return [void]
-
1
def prologue
-
4
pry.exec_hook :before_session, pry.output, pry.current_binding, pry
-
-
# Clear the line before starting Pry. This fixes issue #566.
-
3
if pry.config.correct_indent
-
3
Kernel.print Pry::Helpers::BaseHelpers.windows_ansi? ? "\e[0F" : "\e[0G"
-
end
-
end
-
-
# The actual read-eval-print loop.
-
#
-
# The {REPL} instance is responsible for reading and looping, whereas the
-
# {Pry} instance is responsible for evaluating user input and printing
-
# return values and command output.
-
#
-
# @return [Object?] If the session throws `:breakout`, return the value
-
# thrown with it.
-
# @raise [Exception] If the session throws `:raise_up`, raise the exception
-
# thrown with it.
-
1
def repl
-
3
loop do
-
24
case val = read
-
when :control_c
-
output.puts ""
-
pry.reset_eval_string
-
when :no_more_input
-
output.puts "" if output.tty?
-
break
-
else
-
24
output.puts "" if val.nil? && output.tty?
-
24
return pry.exit_value unless pry.eval(val)
-
end
-
end
-
end
-
-
# Clean up after the repl session.
-
# @return [void]
-
1
def epilogue
-
4
pry.exec_hook :after_session, pry.output, pry.current_binding, pry
-
end
-
-
# Read a line of input from the user.
-
# @return [String] The line entered by the user.
-
# @return [nil] On `<Ctrl-D>`.
-
# @return [:control_c] On `<Ctrl+C>`.
-
# @return [:no_more_input] On EOF.
-
1
def read
-
24
@indent.reset if pry.eval_string.empty?
-
24
current_prompt = pry.select_prompt
-
24
indentation = pry.config.auto_indent ? @indent.current_prefix : ''
-
-
24
val = read_line("#{current_prompt}#{indentation}")
-
-
# Return nil for EOF, :no_more_input for error, or :control_c for <Ctrl-C>
-
24
return val unless String === val
-
-
24
if pry.config.auto_indent
-
24
original_val = "#{indentation}#{val}"
-
24
indented_val = @indent.indent(val)
-
-
24
if output.tty? && pry.config.correct_indent && Pry::Helpers::BaseHelpers.use_ansi_codes?
-
output.print @indent.correct_indentation(
-
current_prompt, indented_val,
-
original_val.length - indented_val.length
-
)
-
output.flush
-
end
-
else
-
indented_val = val
-
end
-
-
24
indented_val
-
end
-
-
# Manage switching of input objects on encountering `EOFError`s.
-
# @return [Object] Whatever the given block returns.
-
# @return [:no_more_input] Indicates that no more input can be read.
-
1
def handle_read_errors
-
24
should_retry = true
-
24
exception_count = 0
-
-
24
begin
-
24
yield
-
rescue EOFError
-
pry.config.input = Pry.config.input
-
if !should_retry
-
output.puts "Error: Pry ran out of things to read from! " \
-
"Attempting to break out of REPL."
-
return :no_more_input
-
end
-
should_retry = false
-
retry
-
-
# Handle <Ctrl+C> like Bash: empty the current input buffer, but don't
-
# quit. This is only for MRI 1.9; other versions of Ruby don't let you
-
# send Interrupt from within Readline.
-
rescue Interrupt
-
return :control_c
-
-
# If we get a random error when trying to read a line we don't want to
-
# automatically retry, as the user will see a lot of error messages
-
# scroll past and be unable to do anything about it.
-
rescue RescuableException => e
-
puts "Error: #{e.message}"
-
output.puts e.backtrace
-
exception_count += 1
-
if exception_count < 5
-
retry
-
end
-
puts "FATAL: Pry failed to get user input using `#{input}`."
-
puts "To fix this you may be able to pass input and output file " \
-
"descriptors to pry directly. e.g."
-
puts " Pry.config.input = STDIN"
-
puts " Pry.config.output = STDOUT"
-
puts " binding.pry"
-
return :no_more_input
-
end
-
end
-
-
# Returns the next line of input to be sent to the {Pry} instance.
-
# @param [String] current_prompt The prompt to use for input.
-
# @return [String?] The next line of input, or `nil` on <Ctrl-D>.
-
1
def read_line(current_prompt)
-
24
handle_read_errors do
-
24
if defined? Coolline and input.is_a? Coolline
-
input.completion_proc = proc do |cool|
-
completions = @pry.complete cool.completed_word
-
completions.compact
-
end
-
elsif input.respond_to? :completion_proc=
-
24
input.completion_proc = proc do |input|
-
@pry.complete input
-
end
-
end
-
-
24
if defined?(Readline) and input == Readline
-
24
input_readline(current_prompt, false) # false since we'll add it manually
-
elsif defined? Coolline and input.is_a? Coolline
-
input_readline(current_prompt)
-
else
-
if input.method(:readline).arity == 1
-
input_readline(current_prompt)
-
else
-
input_readline
-
end
-
end
-
end
-
end
-
-
1
def input_readline(*args)
-
24
Pry::InputLock.for(:all).interruptible_region do
-
24
input.readline(*args)
-
end
-
end
-
end
-
end
-
1
require 'rubygems'
-
-
1
class Pry
-
1
module Rubygem
-
-
1
class << self
-
1
def installed?(name)
-
if Gem::Specification.respond_to?(:find_all_by_name)
-
Gem::Specification.find_all_by_name(name).any?
-
else
-
Gem.source_index.find_name(name).first
-
end
-
end
-
-
# Get the gem spec object for the given gem name.
-
#
-
# @param [String] name
-
# @return [Gem::Specification]
-
1
def spec(name)
-
specs = if Gem::Specification.respond_to?(:each)
-
Gem::Specification.find_all_by_name(name)
-
else
-
Gem.source_index.find_name(name)
-
end
-
-
first_spec = specs.sort_by{ |spec| Gem::Version.new(spec.version) }.last
-
-
first_spec or raise CommandError, "Gem `#{name}` not found"
-
end
-
-
# List gems matching a pattern.
-
#
-
# @param [Regexp] pattern
-
# @return [Array<Gem::Specification>]
-
1
def list(pattern = /.*/)
-
if Gem::Specification.respond_to?(:each)
-
Gem::Specification.select{|spec| spec.name =~ pattern }
-
else
-
Gem.source_index.gems.values.select{|spec| spec.name =~ pattern }
-
end
-
end
-
-
# Completion function for gem-cd and gem-open.
-
#
-
# @param [String] so_far what the user's typed so far
-
# @return [Array<String>] completions
-
1
def complete(so_far)
-
if so_far =~ / ([^ ]*)\z/
-
self.list(%r{\A#{$2}}).map(&:name)
-
else
-
self.list.map(&:name)
-
end
-
end
-
-
# Installs a gem with all its dependencies.
-
#
-
# @param [String] name
-
# @return [void]
-
1
def install(name)
-
gemrc_opts = Gem.configuration['gem'].split(' ')
-
destination = if gemrc_opts.include?('--user-install')
-
Gem.user_dir
-
elsif File.writable?(Gem.dir)
-
Gem.dir
-
else
-
Gem.user_dir
-
end
-
installer = Gem::DependencyInstaller.new(:install_dir => destination)
-
installer.install(name)
-
rescue Errno::EACCES
-
raise CommandError,
-
"Insufficient permissions to install #{ Pry::Helpers::Text.green(name) }."
-
rescue Gem::GemNotFoundException
-
raise CommandError,
-
"Gem #{ Pry::Helpers::Text.green(name) } not found. Aborting installation."
-
else
-
Gem.refresh
-
end
-
end
-
-
end
-
end
-
1
class Pry::Terminal
-
1
class << self
-
# Return a pair of [rows, columns] which gives the size of the window.
-
#
-
# If the window size cannot be determined, return nil.
-
1
def screen_size
-
11
rows, cols = actual_screen_size
-
11
if rows.to_i != 0 && cols.to_i != 0
-
9
[rows.to_i, cols.to_i]
-
else
-
nil
-
end
-
end
-
-
# Return a screen size or a default if that fails.
-
1
def size! default = [27, 80]
-
11
screen_size || default
-
end
-
-
# Return a screen width or the default if that fails.
-
1
def width!
-
6
size![1]
-
end
-
-
# Return a screen height or the default if that fails.
-
1
def height!
-
5
size![0]
-
end
-
-
1
def actual_screen_size
-
# The best way, if possible (requires non-jruby ≥1.9 or io-console gem)
-
screen_size_according_to_io_console or
-
# Fall back to the old standby, though it might be stale:
-
11
screen_size_according_to_env or
-
# Fall further back, though this one is also out of date without something
-
# calling Readline.set_screen_size
-
screen_size_according_to_readline or
-
# Windows users can otherwise run ansicon and get a decent answer:
-
screen_size_according_to_ansicon_env
-
end
-
-
1
def screen_size_according_to_io_console
-
11
return if Pry::Helpers::BaseHelpers.jruby?
-
11
require 'io/console'
-
11
$stdout.winsize if $stdout.tty? and $stdout.respond_to?(:winsize)
-
rescue LoadError
-
# They probably don't have the io/console stdlib or the io-console gem.
-
# We'll keep trying.
-
end
-
-
1
def screen_size_according_to_env
-
11
size = [ENV['LINES'] || ENV['ROWS'], ENV['COLUMNS']]
-
11
size if nonzero_column?(size)
-
end
-
-
1
def screen_size_according_to_readline
-
2
if defined?(Readline) && Readline.respond_to?(:get_screen_size)
-
2
size = Readline.get_screen_size
-
2
size if nonzero_column?(size)
-
end
-
rescue Java::JavaLang::NullPointerException
-
# This rescue won't happen on jrubies later than:
-
# https://github.com/jruby/jruby/pull/436
-
nil
-
end
-
-
1
def screen_size_according_to_ansicon_env
-
2
return unless ENV['ANSICON'] =~ /\((.*)x(.*)\)/
-
size = [$2, $1]
-
size if nonzero_column?(size)
-
end
-
-
1
private
-
-
1
def nonzero_column?(size)
-
13
size[1].to_i > 0
-
end
-
end
-
end
-
1
class Pry
-
1
VERSION = "0.10.1"
-
end
-
1
require 'pry/module_candidate'
-
-
1
class Pry
-
1
class << self
-
# If the given object is a `Pry::WrappedModule`, return it unaltered. If it's
-
# anything else, return it wrapped in a `Pry::WrappedModule` instance.
-
1
def WrappedModule(obj)
-
if obj.is_a? Pry::WrappedModule
-
obj
-
else
-
Pry::WrappedModule.new(obj)
-
end
-
end
-
end
-
-
1
class WrappedModule
-
1
include Helpers::BaseHelpers
-
1
include CodeObject::Helpers
-
-
1
attr_reader :wrapped
-
-
# Convert a string to a module.
-
#
-
# @param [String] mod_name
-
# @param [Binding] target The binding where the lookup takes place.
-
# @return [Module, nil] The module or `nil` (if conversion failed).
-
# @example
-
# Pry::WrappedModule.from_str("Pry::Code")
-
1
def self.from_str(mod_name, target=TOPLEVEL_BINDING)
-
if safe_to_evaluate?(mod_name, target)
-
Pry::WrappedModule.new(target.eval(mod_name))
-
else
-
nil
-
end
-
rescue RescuableException
-
nil
-
end
-
-
1
class << self
-
1
private
-
-
# We use this method to decide whether code is safe to eval. Method's are
-
# generally not, but everything else is.
-
# TODO: is just checking != "method" enough??
-
# TODO: see duplication of this method in Pry::CodeObject
-
# @param [String] str The string to lookup.
-
# @param [Binding] target Where the lookup takes place.
-
# @return [Boolean]
-
1
def safe_to_evaluate?(str, target)
-
return true if str.strip == "self"
-
kind = target.eval("defined?(#{str})")
-
kind =~ /variable|constant/
-
end
-
end
-
-
# @raise [ArgumentError] if the argument is not a `Module`
-
# @param [Module] mod
-
1
def initialize(mod)
-
4
raise ArgumentError, "Tried to initialize a WrappedModule with a non-module #{mod.inspect}" unless ::Module === mod
-
4
@wrapped = mod
-
4
@memoized_candidates = []
-
4
@host_file_lines = nil
-
4
@source = nil
-
4
@source_location = nil
-
4
@doc = nil
-
end
-
-
# Returns an array of the names of the constants accessible in the wrapped
-
# module. This avoids the problem of accidentally calling the singleton
-
# method `Module.constants`.
-
# @param [Boolean] inherit Include the names of constants from included
-
# modules?
-
1
def constants(inherit = true)
-
Module.instance_method(:constants).bind(@wrapped).call(inherit)
-
end
-
-
# The prefix that would appear before methods defined on this class.
-
#
-
# i.e. the "String." or "String#" in String.new and String#initialize.
-
#
-
# @return String
-
1
def method_prefix
-
4
if singleton_class?
-
if Module === singleton_instance
-
"#{WrappedModule.new(singleton_instance).nonblank_name}."
-
else
-
"self."
-
end
-
else
-
4
"#{nonblank_name}#"
-
end
-
end
-
-
# The name of the Module if it has one, otherwise #<Class:0xf00>.
-
#
-
# @return [String]
-
1
def nonblank_name
-
4
if name.to_s == ""
-
wrapped.inspect
-
else
-
4
name
-
end
-
end
-
-
# Is this a singleton class?
-
# @return [Boolean]
-
1
def singleton_class?
-
4
if Pry::Method.safe_send(wrapped, :respond_to?, :singleton_class?)
-
4
Pry::Method.safe_send(wrapped, :singleton_class?)
-
elsif defined?(Rubinius)
-
# https://github.com/rubinius/rubinius/commit/2e71722dba53d1a92c54d5e3968d64d1042486fe singleton_class? added 30 Jul 2014
-
# https://github.com/rubinius/rubinius/commit/4310f6b2ef3c8fc88135affe697db4e29e4621c4 has been around since 2011
-
!!Rubinius::Type.singleton_class_object(wrapped)
-
else
-
wrapped != Pry::Method.safe_send(wrapped, :ancestors).first
-
end
-
end
-
-
# Is this strictly a module? (does not match classes)
-
# @return [Boolean]
-
1
def module?
-
wrapped.instance_of?(Module)
-
end
-
-
# Is this strictly a class?
-
# @return [Boolean]
-
1
def class?
-
wrapped.instance_of?(Class)
-
end
-
-
# Get the instance associated with this singleton class.
-
#
-
# @raise ArgumentError: tried to get instance of non singleton class
-
#
-
# @return [Object]
-
1
def singleton_instance
-
raise ArgumentError, "tried to get instance of non singleton class" unless singleton_class?
-
-
if Helpers::BaseHelpers.jruby?
-
wrapped.to_java.attached
-
else
-
@singleton_instance ||= ObjectSpace.each_object(wrapped).detect{ |x| (class << x; self; end) == wrapped }
-
end
-
end
-
-
# Forward method invocations to the wrapped module
-
1
def method_missing(method_name, *args, &block)
-
8
wrapped.send(method_name, *args, &block)
-
end
-
-
1
def respond_to?(method_name)
-
super || wrapped.respond_to?(method_name)
-
end
-
-
# Retrieve the source location of a module. Return value is in same
-
# format as Method#source_location. If the source location
-
# cannot be found this method returns `nil`.
-
#
-
# @param [Module] mod The module (or class).
-
# @return [Array<String, Fixnum>, nil] The source location of the
-
# module (or class), or `nil` if no source location found.
-
1
def source_location
-
@source_location ||= primary_candidate.source_location
-
rescue Pry::RescuableException
-
nil
-
end
-
-
# @return [String, nil] The associated file for the module (i.e
-
# the primary candidate: highest ranked monkeypatch).
-
1
def file
-
Array(source_location).first
-
end
-
1
alias_method :source_file, :file
-
-
# @return [Fixnum, nil] The associated line for the module (i.e
-
# the primary candidate: highest ranked monkeypatch).
-
1
def line
-
Array(source_location).last
-
end
-
1
alias_method :source_line, :line
-
-
# Returns documentation for the module.
-
# This documentation is for the primary candidate, if
-
# you would like documentation for other candidates use
-
# `WrappedModule#candidate` to select the candidate you're
-
# interested in.
-
# @raise [Pry::CommandError] If documentation cannot be found.
-
# @return [String] The documentation for the module.
-
1
def doc
-
@doc ||= primary_candidate.doc
-
end
-
-
# Returns the source for the module.
-
# This source is for the primary candidate, if
-
# you would like source for other candidates use
-
# `WrappedModule#candidate` to select the candidate you're
-
# interested in.
-
# @raise [Pry::CommandError] If source cannot be found.
-
# @return [String] The source for the module.
-
1
def source
-
@source ||= primary_candidate.source
-
end
-
-
# @return [String] Return the associated file for the
-
# module from YARD, if one exists.
-
1
def yard_file
-
YARD::Registry.at(name).file if yard_docs?
-
end
-
-
# @return [Fixnum] Return the associated line for the
-
# module from YARD, if one exists.
-
1
def yard_line
-
YARD::Registry.at(name).line if yard_docs?
-
end
-
-
# @return [String] Return the YARD docs for this module.
-
1
def yard_doc
-
YARD::Registry.at(name).docstring.to_s if yard_docs?
-
end
-
-
# Return a candidate for this module of specified rank. A `rank`
-
# of 0 is equivalent to the 'primary candidate', which is the
-
# module definition with the highest number of methods. A `rank`
-
# of 1 is the module definition with the second highest number of
-
# methods, and so on. Module candidates are necessary as modules
-
# can be reopened multiple times and in multiple places in Ruby,
-
# the candidate API gives you access to the module definition
-
# representing each of those reopenings.
-
# @raise [Pry::CommandError] If the `rank` is out of range. That
-
# is greater than `number_of_candidates - 1`.
-
# @param [Fixnum] rank
-
# @return [Pry::WrappedModule::Candidate]
-
1
def candidate(rank)
-
@memoized_candidates[rank] ||= Candidate.new(self, rank)
-
end
-
-
# @return [Fixnum] The number of candidate definitions for the
-
# current module.
-
1
def number_of_candidates
-
method_candidates.count
-
end
-
-
# @note On JRuby 1.9 and higher, in certain conditions, this method chucks
-
# away its ability to be quick (when there are lots of monkey patches,
-
# like in Rails). However, it should be efficient enough on other rubies.
-
# @see https://github.com/jruby/jruby/issues/525
-
# @return [Enumerator, Array] on JRuby 1.9 and higher returns Array, on
-
# other rubies returns Enumerator
-
1
def candidates
-
enum = Enumerator.new do |y|
-
(0...number_of_candidates).each do |num|
-
y.yield candidate(num)
-
end
-
end
-
Pry::Helpers::BaseHelpers.jruby_19? ? enum.to_a : enum
-
end
-
-
# @return [Boolean] Whether YARD docs are available for this module.
-
1
def yard_docs?
-
!!(defined?(YARD) && YARD::Registry.at(name))
-
end
-
-
# @param [Fixnum] times How far to travel up the ancestor chain.
-
# @return [Pry::WrappedModule, nil] The wrapped module that is the
-
# superclass.
-
# When `self` is a `Module` then return the
-
# nth ancestor, otherwise (in the case of classes) return the
-
# nth ancestor that is a class.
-
1
def super(times=1)
-
return self if times.zero?
-
-
if wrapped.is_a?(Class)
-
sup = ancestors.select { |v| v.is_a?(Class) }[times]
-
else
-
sup = ancestors[times]
-
end
-
-
Pry::WrappedModule(sup) if sup
-
end
-
-
1
private
-
-
# @return [Pry::WrappedModule::Candidate] The candidate with the
-
# highest rank, that is the 'monkey patch' of this module with the
-
# highest number of methods, which contains a source code line that
-
# defines the module. It is considered the 'canonical' definition
-
# for the module. In the absense of a suitable candidate, the
-
# candidate of rank 0 will be returned, or a CommandError raised if
-
# there are no candidates at all.
-
1
def primary_candidate
-
@primary_candidate ||= candidates.find { |c| c.file } ||
-
# This will raise an exception if there is no candidate at all.
-
candidate(0)
-
end
-
-
# @return [Array<Array<Pry::Method>>] The array of `Pry::Method` objects,
-
# there are two associated with each candidate. The first is the 'base
-
# method' for a candidate and it serves as the start point for
-
# the search in uncovering the module definition. The second is
-
# the last method defined for that candidate and it is used to
-
# speed up source code extraction.
-
1
def method_candidates
-
@method_candidates ||= all_source_locations_by_popularity.map do |group|
-
methods_sorted_by_source_line = group.last.sort_by(&:source_line)
-
[methods_sorted_by_source_line.first, methods_sorted_by_source_line.last]
-
end
-
end
-
-
# A helper method.
-
1
def all_source_locations_by_popularity
-
return @all_source_locations_by_popularity if @all_source_locations_by_popularity
-
-
ims = all_relevant_methods_for(wrapped)
-
@all_source_locations_by_popularity = ims.group_by { |v| Array(v.source_location).first }.
-
sort_by do |path, methods|
-
expanded = File.expand_path(path)
-
load_order = $LOADED_FEATURES.index{ |file| expanded.end_with?(file) }
-
-
[-methods.size, load_order || (1.0 / 0.0)]
-
end
-
end
-
-
# We only want methods that have a non-nil `source_location`. We also
-
# skip some spooky internal methods.
-
# (i.e we skip `__class_init__` because it's an odd rbx specific thing that causes tests to fail.)
-
# @return [Array<Pry::Method>]
-
1
def all_relevant_methods_for(mod)
-
methods = all_methods_for(mod).select(&:source_location).
-
reject{ |x| x.name == '__class_init__' || method_defined_by_forwardable_module?(x) }
-
-
return methods unless methods.empty?
-
-
safe_send(mod, :constants).map do |const_name|
-
if const = nested_module?(mod, const_name)
-
all_relevant_methods_for(const)
-
else
-
[]
-
end
-
end.flatten
-
end
-
-
# Return all methods (instance methods and class methods) for a
-
# given module.
-
# @return [Array<Pry::Method>]
-
1
def all_methods_for(mod)
-
Pry::Method.all_from_obj(mod, false) + Pry::Method.all_from_class(mod, false)
-
end
-
-
1
def nested_module?(parent, name)
-
return if safe_send(parent, :autoload?, name)
-
child = safe_send(parent, :const_get, name)
-
return unless Module === child
-
return unless safe_send(child, :name) == "#{safe_send(parent, :name)}::#{name}"
-
child
-
end
-
-
# Detect methods that are defined with `def_delegator` from the Forwardable
-
# module. We want to reject these methods as they screw up module
-
# extraction since the `source_location` for such methods points at forwardable.rb
-
# TODO: make this more robust as valid user-defined files called
-
# forwardable.rb are also skipped.
-
1
def method_defined_by_forwardable_module?(method)
-
method.source_location.first =~ /forwardable\.rb/
-
end
-
-
# memoized lines for file
-
1
def lines_for_file(file)
-
@lines_for_file ||= {}
-
-
if file == Pry.eval_path
-
@lines_for_file[file] ||= Pry.line_buffer.drop(1)
-
else
-
@lines_for_file[file] ||= File.readlines(file)
-
end
-
end
-
end
-
end
-
1
RSpec::Support.require_rspec_core "formatters/helpers"
-
1
require 'stringio'
-
-
1
module RSpec
-
1
module Core
-
1
module Formatters
-
# RSpec's built-in formatters are all subclasses of
-
# RSpec::Core::Formatters::BaseTextFormatter.
-
#
-
# @see RSpec::Core::Formatters::BaseTextFormatter
-
# @see RSpec::Core::Reporter
-
# @see RSpec::Core::Formatters::Protocol
-
1
class BaseFormatter
-
# All formatters inheriting from this formatter will receive these
-
# notifications.
-
1
Formatters.register self, :start, :example_group_started, :close
-
1
attr_accessor :example_group
-
1
attr_reader :output
-
-
# @api public
-
# @param output [IO] the formatter output
-
# @see RSpec::Core::Formatters::Protocol#initialize
-
1
def initialize(output)
-
2
@output = output || StringIO.new
-
2
@example_group = nil
-
end
-
-
# @api public
-
#
-
# @param notification [StartNotification]
-
# @see RSpec::Core::Formatters::Protocol#start
-
1
def start(notification)
-
2
start_sync_output
-
2
@example_count = notification.count
-
end
-
-
# @api public
-
#
-
# @param notification [GroupNotification] containing example_group
-
# subclass of `RSpec::Core::ExampleGroup`
-
# @see RSpec::Core::Formatters::Protocol#example_group_started
-
1
def example_group_started(notification)
-
3
@example_group = notification.group
-
end
-
-
# @api public
-
#
-
# @param _notification [NullNotification] (Ignored)
-
# @see RSpec::Core::Formatters::Protocol#close
-
1
def close(_notification)
-
restore_sync_output
-
end
-
-
1
private
-
-
1
def start_sync_output
-
2
@old_sync, output.sync = output.sync, true if output_supports_sync
-
end
-
-
1
def restore_sync_output
-
output.sync = @old_sync if output_supports_sync && !output.closed?
-
end
-
-
1
def output_supports_sync
-
2
output.respond_to?(:sync=)
-
end
-
end
-
end
-
end
-
end
-
1
RSpec::Support.require_rspec_core "formatters/base_formatter"
-
1
RSpec::Support.require_rspec_core "formatters/console_codes"
-
-
1
module RSpec
-
1
module Core
-
1
module Formatters
-
# Base for all of RSpec's built-in formatters. See
-
# RSpec::Core::Formatters::BaseFormatter to learn more about all of the
-
# methods called by the reporter.
-
#
-
# @see RSpec::Core::Formatters::BaseFormatter
-
# @see RSpec::Core::Reporter
-
1
class BaseTextFormatter < BaseFormatter
-
1
Formatters.register self,
-
:message, :dump_summary, :dump_failures, :dump_pending, :seed
-
-
# @api public
-
#
-
# Used by the reporter to send messages to the output stream.
-
#
-
# @param notification [MessageNotification] containing message
-
1
def message(notification)
-
output.puts notification.message
-
end
-
-
# @api public
-
#
-
# Dumps detailed information about each example failure.
-
#
-
# @param notification [NullNotification]
-
1
def dump_failures(notification)
-
1
return if notification.failure_notifications.empty?
-
1
output.puts notification.fully_formatted_failed_examples
-
end
-
-
# @api public
-
#
-
# This method is invoked after the dumping of examples and failures.
-
# Each parameter is assigned to a corresponding attribute.
-
#
-
# @param summary [SummaryNotification] containing duration,
-
# example_count, failure_count and pending_count
-
1
def dump_summary(summary)
-
output.puts summary.fully_formatted
-
end
-
-
# @private
-
1
def dump_pending(notification)
-
1
return if notification.pending_examples.empty?
-
output.puts notification.fully_formatted_pending_examples
-
end
-
-
# @private
-
1
def seed(notification)
-
1
return unless notification.seed_used?
-
output.puts notification.fully_formatted
-
end
-
-
# @api public
-
#
-
# Invoked at the very end, `close` allows the formatter to clean
-
# up resources, e.g. open streams, etc.
-
#
-
# @param _notification [NullNotification] (Ignored)
-
1
def close(_notification)
-
1
return unless IO === output
-
1
return if output.closed?
-
-
1
output.puts
-
-
output.flush
-
output.close unless output == $stdout
-
end
-
end
-
end
-
end
-
end
-
1
module RSpec
-
1
module Core
-
1
module Formatters
-
# ConsoleCodes provides helpers for formatting console output
-
# with ANSI codes, e.g. color's and bold.
-
1
module ConsoleCodes
-
# @private
-
1
VT100_CODES =
-
{
-
:black => 30,
-
:red => 31,
-
:green => 32,
-
:yellow => 33,
-
:blue => 34,
-
:magenta => 35,
-
:cyan => 36,
-
:white => 37,
-
:bold => 1,
-
}
-
# @private
-
1
VT100_CODE_VALUES = VT100_CODES.invert
-
-
1
module_function
-
-
# @private
-
1
CONFIG_COLORS_TO_METHODS = Configuration.instance_methods.grep(/_color\z/).inject({}) do |hash, method|
-
6
hash[method.to_s.sub(/_color\z/, '').to_sym] = method
-
6
hash
-
end
-
-
# Fetches the correct code for the supplied symbol, or checks
-
# that a code is valid. Defaults to white (37).
-
#
-
# @param code_or_symbol [Symbol, Fixnum] Symbol or code to check
-
# @return [Fixnum] a console code
-
1
def console_code_for(code_or_symbol)
-
2
if (config_method = CONFIG_COLORS_TO_METHODS[code_or_symbol])
-
1
console_code_for RSpec.configuration.__send__(config_method)
-
1
elsif VT100_CODE_VALUES.key?(code_or_symbol)
-
code_or_symbol
-
else
-
1
VT100_CODES.fetch(code_or_symbol) do
-
console_code_for(:white)
-
end
-
end
-
end
-
-
# Wraps a piece of text in ANSI codes with the supplied code. Will
-
# only apply the control code if `RSpec.configuration.color_enabled?`
-
# returns true.
-
#
-
# @param text [String] the text to wrap
-
# @param code_or_symbol [Symbol, Fixnum] the desired control code
-
# @return [String] the wrapped text
-
1
def wrap(text, code_or_symbol)
-
257
if RSpec.configuration.color_enabled?
-
1
"\e[#{console_code_for(code_or_symbol)}m#{text}\e[0m"
-
else
-
256
text
-
end
-
end
-
end
-
end
-
end
-
end
-
1
RSpec::Support.require_rspec_core "formatters/base_text_formatter"
-
-
1
module RSpec
-
1
module Core
-
1
module Formatters
-
# @private
-
1
class DocumentationFormatter < BaseTextFormatter
-
1
Formatters.register self, :example_group_started, :example_group_finished,
-
:example_passed, :example_pending, :example_failed
-
-
1
def initialize(output)
-
1
super
-
1
@group_level = 0
-
end
-
-
1
def example_group_started(notification)
-
4
output.puts if @group_level == 0
-
4
output.puts "#{current_indentation}#{notification.group.description.strip}"
-
-
3
@group_level += 1
-
end
-
-
1
def example_group_finished(_notification)
-
3
@group_level -= 1
-
end
-
-
1
def example_passed(passed)
-
1
output.puts passed_output(passed.example)
-
end
-
-
1
def example_pending(pending)
-
output.puts pending_output(pending.example,
-
pending.example.execution_result.pending_message)
-
end
-
-
1
def example_failed(failure)
-
2
output.puts failure_output(failure.example,
-
failure.example.execution_result.exception)
-
end
-
-
1
private
-
-
1
def passed_output(example)
-
1
ConsoleCodes.wrap("#{current_indentation}#{example.description.strip}", :success)
-
end
-
-
1
def pending_output(example, message)
-
ConsoleCodes.wrap("#{current_indentation}#{example.description.strip} " \
-
"(PENDING: #{message})",
-
:pending)
-
end
-
-
1
def failure_output(example, _exception)
-
2
ConsoleCodes.wrap("#{current_indentation}#{example.description.strip} " \
-
"(FAILED - #{next_failure_index})",
-
:failure)
-
end
-
-
1
def next_failure_index
-
2
@next_failure_index ||= 0
-
2
@next_failure_index += 1
-
end
-
-
1
def current_indentation
-
7
' ' * @group_level
-
end
-
end
-
end
-
end
-
end
-
1
RSpec::Support.require_rspec_core "formatters/base_formatter"
-
1
require 'json'
-
-
1
module RSpec
-
1
module Core
-
1
module Formatters
-
# @private
-
1
class JsonFormatter < BaseFormatter
-
1
Formatters.register self, :message, :dump_summary, :dump_profile, :stop, :close
-
-
1
attr_reader :output_hash
-
-
1
def initialize(output)
-
1
super
-
1
@output_hash = {
-
:version => RSpec::Core::Version::STRING
-
}
-
end
-
-
1
def message(notification)
-
(@output_hash[:messages] ||= []) << notification.message
-
end
-
-
1
def dump_summary(summary)
-
@output_hash[:summary] = {
-
:duration => summary.duration,
-
:example_count => summary.example_count,
-
:failure_count => summary.failure_count,
-
:pending_count => summary.pending_count
-
}
-
@output_hash[:summary_line] = summary.totals_line
-
end
-
-
1
def stop(notification)
-
1
@output_hash[:examples] = notification.examples.map do |example|
-
3
format_example(example).tap do |hash|
-
3
e = example.exception
-
3
if e
-
2
hash[:exception] = {
-
:class => e.class.name,
-
:message => e.message,
-
:backtrace => e.backtrace,
-
}
-
end
-
end
-
end
-
end
-
-
1
def close(_notification)
-
output.write @output_hash.to_json
-
output.close if IO === output && output != $stdout
-
end
-
-
1
def dump_profile(profile)
-
@output_hash[:profile] = {}
-
dump_profile_slowest_examples(profile)
-
dump_profile_slowest_example_groups(profile)
-
end
-
-
# @api private
-
1
def dump_profile_slowest_examples(profile)
-
@output_hash[:profile] = {}
-
@output_hash[:profile][:examples] = profile.slowest_examples.map do |example|
-
format_example(example).tap do |hash|
-
hash[:run_time] = example.execution_result.run_time
-
end
-
end
-
@output_hash[:profile][:slowest] = profile.slow_duration
-
@output_hash[:profile][:total] = profile.duration
-
end
-
-
# @api private
-
1
def dump_profile_slowest_example_groups(profile)
-
@output_hash[:profile] ||= {}
-
@output_hash[:profile][:groups] = profile.slowest_groups.map do |loc, hash|
-
hash.update(:location => loc)
-
end
-
end
-
-
1
private
-
-
1
def format_example(example)
-
{
-
:description => example.description,
-
:full_description => example.full_description,
-
:status => example.execution_result.status.to_s,
-
:file_path => example.metadata[:file_path],
-
:line_number => example.metadata[:line_number],
-
:run_time => example.execution_result.run_time,
-
:pending_message => example.execution_result.pending_message,
-
3
}
-
end
-
end
-
end
-
end
-
end
-
1
require 'rspec/mocks'
-
-
1
module RSpec
-
1
module Core
-
1
module MockingAdapters
-
# @private
-
1
module RSpec
-
1
include ::RSpec::Mocks::ExampleMethods
-
-
1
def self.framework_name
-
1
:rspec
-
end
-
-
1
def self.configuration
-
::RSpec::Mocks.configuration
-
end
-
-
1
def setup_mocks_for_rspec
-
2
::RSpec::Mocks.setup
-
end
-
-
1
def verify_mocks_for_rspec
-
1
::RSpec::Mocks.verify
-
end
-
-
1
def teardown_mocks_for_rspec
-
2
::RSpec::Mocks.teardown
-
end
-
end
-
end
-
end
-
end
-
1
require 'rspec/support'
-
1
RSpec::Support.require_rspec_support "caller_filter"
-
1
RSpec::Support.require_rspec_support "warnings"
-
1
RSpec::Support.require_rspec_support "object_formatter"
-
-
1
require 'rspec/matchers'
-
-
7
RSpec::Support.define_optimized_require_for_rspec(:expectations) { |f| require_relative(f) }
-
-
%w[
-
expectation_target
-
configuration
-
fail_with
-
handler
-
version
-
6
].each { |file| RSpec::Support.require_rspec_expectations(file) }
-
-
1
module RSpec
-
# RSpec::Expectations provides a simple, readable API to express
-
# the expected outcomes in a code example. To express an expected
-
# outcome, wrap an object or block in `expect`, call `to` or `to_not`
-
# (aliased as `not_to`) and pass it a matcher object:
-
#
-
# expect(order.total).to eq(Money.new(5.55, :USD))
-
# expect(list).to include(user)
-
# expect(message).not_to match(/foo/)
-
# expect { do_something }.to raise_error
-
#
-
# The last form (the block form) is needed to match against ruby constructs
-
# that are not objects, but can only be observed when executing a block
-
# of code. This includes raising errors, throwing symbols, yielding,
-
# and changing values.
-
#
-
# When `expect(...).to` is invoked with a matcher, it turns around
-
# and calls `matcher.matches?(<object wrapped by expect>)`. For example,
-
# in the expression:
-
#
-
# expect(order.total).to eq(Money.new(5.55, :USD))
-
#
-
# ...`eq(Money.new(5.55, :USD))` returns a matcher object, and it results
-
# in the equivalent of `eq.matches?(order.total)`. If `matches?` returns
-
# `true`, the expectation is met and execution continues. If `false`, then
-
# the spec fails with the message returned by `eq.failure_message`.
-
#
-
# Given the expression:
-
#
-
# expect(order.entries).not_to include(entry)
-
#
-
# ...the `not_to` method (also available as `to_not`) invokes the equivalent of
-
# `include.matches?(order.entries)`, but it interprets `false` as success, and
-
# `true` as a failure, using the message generated by
-
# `include.failure_message_when_negated`.
-
#
-
# rspec-expectations ships with a standard set of useful matchers, and writing
-
# your own matchers is quite simple.
-
#
-
# See [RSpec::Matchers](../RSpec/Matchers) for more information about the
-
# built-in matchers that ship with rspec-expectations, and how to write your
-
# own custom matchers.
-
1
module Expectations
-
# Exception raised when an expectation fails.
-
#
-
# @note We subclass Exception so that in a stub implementation if
-
# the user sets an expectation, it can't be caught in their
-
# code by a bare `rescue`.
-
# @api public
-
1
class ExpectationNotMetError < Exception
-
end
-
-
# Exception raised from `aggregate_failures` when multiple expectations fail.
-
#
-
# @note The constant is defined here but the extensive logic of this class
-
# is lazily defined when `FailureAggregator` is autoloaded, since we do
-
# not need to waste time defining that functionality unless
-
# `aggregate_failures` is used.
-
1
class MultipleExpectationsNotMetError < ExpectationNotMetError
-
end
-
-
1
autoload :FailureAggregator, "rspec/expectations/failure_aggregator"
-
end
-
end
-
1
RSpec::Support.require_rspec_expectations "syntax"
-
-
1
module RSpec
-
1
module Expectations
-
# Provides configuration options for rspec-expectations.
-
# If you are using rspec-core, you can access this via a
-
# block passed to `RSpec::Core::Configuration#expect_with`.
-
# Otherwise, you can access it via RSpec::Expectations.configuration.
-
#
-
# @example
-
# RSpec.configure do |rspec|
-
# rspec.expect_with :rspec do |c|
-
# # c is the config object
-
# end
-
# end
-
#
-
# # or
-
#
-
# RSpec::Expectations.configuration
-
1
class Configuration
-
1
def initialize
-
1
@warn_about_potential_false_positives = true
-
end
-
-
# Configures the supported syntax.
-
# @param [Array<Symbol>, Symbol] values the syntaxes to enable
-
# @example
-
# RSpec.configure do |rspec|
-
# rspec.expect_with :rspec do |c|
-
# c.syntax = :should
-
# # or
-
# c.syntax = :expect
-
# # or
-
# c.syntax = [:should, :expect]
-
# end
-
# end
-
1
def syntax=(values)
-
1
if Array(values).include?(:expect)
-
1
Expectations::Syntax.enable_expect
-
else
-
Expectations::Syntax.disable_expect
-
end
-
-
1
if Array(values).include?(:should)
-
1
Expectations::Syntax.enable_should
-
else
-
Expectations::Syntax.disable_should
-
end
-
end
-
-
# The list of configured syntaxes.
-
# @return [Array<Symbol>] the list of configured syntaxes.
-
# @example
-
# unless RSpec::Matchers.configuration.syntax.include?(:expect)
-
# raise "this RSpec extension gem requires the rspec-expectations `:expect` syntax"
-
# end
-
1
def syntax
-
syntaxes = []
-
syntaxes << :should if Expectations::Syntax.should_enabled?
-
syntaxes << :expect if Expectations::Syntax.expect_enabled?
-
syntaxes
-
end
-
-
1
if ::RSpec.respond_to?(:configuration)
-
1
def color?
-
::RSpec.configuration.color_enabled?
-
end
-
else
-
# Indicates whether or not diffs should be colored.
-
# Delegates to rspec-core's color option if rspec-core
-
# is loaded; otherwise you can set it here.
-
attr_writer :color
-
-
# Indicates whether or not diffs should be colored.
-
# Delegates to rspec-core's color option if rspec-core
-
# is loaded; otherwise you can set it here.
-
def color?
-
defined?(@color) && @color
-
end
-
end
-
-
# Adds `should` and `should_not` to the given classes
-
# or modules. This can be used to ensure `should` works
-
# properly on things like proxy objects (particular
-
# `Delegator`-subclassed objects on 1.8).
-
#
-
# @param [Array<Module>] modules the list of classes or modules
-
# to add `should` and `should_not` to.
-
1
def add_should_and_should_not_to(*modules)
-
modules.each do |mod|
-
Expectations::Syntax.enable_should(mod)
-
end
-
end
-
-
# Sets or gets the backtrace formatter. The backtrace formatter should
-
# implement `#format_backtrace(Array<String>)`. This is used
-
# to format backtraces of errors handled by the `raise_error`
-
# matcher.
-
#
-
# If you are using rspec-core, rspec-core's backtrace formatting
-
# will be used (including respecting the presence or absence of
-
# the `--backtrace` option).
-
#
-
# @!attribute [rw] backtrace_formatter
-
1
attr_writer :backtrace_formatter
-
1
def backtrace_formatter
-
@backtrace_formatter ||= if defined?(::RSpec.configuration.backtrace_formatter)
-
::RSpec.configuration.backtrace_formatter
-
else
-
NullBacktraceFormatter
-
end
-
end
-
-
# Sets if custom matcher descriptions and failure messages
-
# should include clauses from methods defined using `chain`.
-
# @param value [Boolean]
-
1
attr_writer :include_chain_clauses_in_custom_matcher_descriptions
-
-
# Indicates whether or not custom matcher descriptions and failure messages
-
# should include clauses from methods defined using `chain`. It is
-
# false by default for backwards compatibility.
-
1
def include_chain_clauses_in_custom_matcher_descriptions?
-
@include_chain_clauses_in_custom_matcher_descriptions ||= false
-
end
-
-
# @private
-
1
def reset_syntaxes_to_default
-
1
self.syntax = [:should, :expect]
-
1
RSpec::Expectations::Syntax.warn_about_should!
-
end
-
-
# @api private
-
# Null implementation of a backtrace formatter used by default
-
# when rspec-core is not loaded. Does no filtering.
-
1
NullBacktraceFormatter = Module.new do
-
1
def self.format_backtrace(backtrace)
-
backtrace
-
end
-
end
-
-
# Configures whether RSpec will warn about matcher use which will
-
# potentially cause false positives in tests.
-
#
-
# @param value [Boolean]
-
1
attr_writer :warn_about_potential_false_positives
-
-
# Indicates whether RSpec will warn about matcher use which will
-
# potentially cause false positives in tests, generally you want to
-
# avoid such scenarios so this defaults to `true`.
-
1
def warn_about_potential_false_positives?
-
@warn_about_potential_false_positives
-
end
-
end
-
-
# The configuration object.
-
# @return [RSpec::Expectations::Configuration] the configuration object
-
1
def self.configuration
-
1
@configuration ||= Configuration.new
-
end
-
-
# set default syntax
-
1
configuration.reset_syntaxes_to_default
-
end
-
end
-
1
module RSpec
-
1
module Expectations
-
# Wraps the target of an expectation.
-
#
-
# @example
-
# expect(something) # => ExpectationTarget wrapping something
-
# expect { do_something } # => ExpectationTarget wrapping the block
-
#
-
# # used with `to`
-
# expect(actual).to eq(3)
-
#
-
# # with `not_to`
-
# expect(actual).not_to eq(3)
-
#
-
# @note `ExpectationTarget` is not intended to be instantiated
-
# directly by users. Use `expect` instead.
-
1
class ExpectationTarget
-
# @private
-
# Used as a sentinel value to be able to tell when the user
-
# did not pass an argument. We can't use `nil` for that because
-
# `nil` is a valid value to pass.
-
1
UndefinedValue = Module.new
-
-
# @api private
-
1
def initialize(value)
-
9
@target = value
-
end
-
-
# @private
-
1
def self.for(value, block)
-
9
if UndefinedValue.equal?(value)
-
unless block
-
raise ArgumentError, "You must pass either an argument or a block to `expect`."
-
end
-
BlockExpectationTarget.new(block)
-
9
elsif block
-
raise ArgumentError, "You cannot pass both an argument and a block to `expect`."
-
else
-
9
new(value)
-
end
-
end
-
-
# Runs the given expectation, passing if `matcher` returns true.
-
# @example
-
# expect(value).to eq(5)
-
# expect { perform }.to raise_error
-
# @param [Matcher]
-
# matcher
-
# @param [String or Proc] message optional message to display when the expectation fails
-
# @return [Boolean] true if the expectation succeeds (else raises)
-
# @see RSpec::Matchers
-
1
def to(matcher=nil, message=nil, &block)
-
9
prevent_operator_matchers(:to) unless matcher
-
9
RSpec::Expectations::PositiveExpectationHandler.handle_matcher(@target, matcher, message, &block)
-
end
-
-
# Runs the given expectation, passing if `matcher` returns false.
-
# @example
-
# expect(value).not_to eq(5)
-
# @param [Matcher]
-
# matcher
-
# @param [String or Proc] message optional message to display when the expectation fails
-
# @return [Boolean] false if the negative expectation succeeds (else raises)
-
# @see RSpec::Matchers
-
1
def not_to(matcher=nil, message=nil, &block)
-
prevent_operator_matchers(:not_to) unless matcher
-
RSpec::Expectations::NegativeExpectationHandler.handle_matcher(@target, matcher, message, &block)
-
end
-
1
alias to_not not_to
-
-
1
private
-
-
1
def prevent_operator_matchers(verb)
-
raise ArgumentError, "The expect syntax does not support operator matchers, " \
-
"so you must pass a matcher to `##{verb}`."
-
end
-
end
-
-
# @private
-
# Validates the provided matcher to ensure it supports block
-
# expectations, in order to avoid user confusion when they
-
# use a block thinking the expectation will be on the return
-
# value of the block rather than the block itself.
-
1
class BlockExpectationTarget < ExpectationTarget
-
1
def to(matcher, message=nil, &block)
-
enforce_block_expectation(matcher)
-
super
-
end
-
-
1
def not_to(matcher, message=nil, &block)
-
enforce_block_expectation(matcher)
-
super
-
end
-
1
alias to_not not_to
-
-
1
private
-
-
1
def enforce_block_expectation(matcher)
-
return if supports_block_expectations?(matcher)
-
-
raise ExpectationNotMetError, "You must pass an argument rather than a block to use the provided " \
-
"matcher (#{RSpec::Support::ObjectFormatter.format(matcher)}), or the matcher must implement " \
-
"`supports_block_expectations?`."
-
end
-
-
1
def supports_block_expectations?(matcher)
-
matcher.supports_block_expectations?
-
rescue NoMethodError
-
false
-
end
-
end
-
end
-
end
-
1
module RSpec
-
1
module Expectations
-
1
class << self
-
# @private
-
1
def differ
-
RSpec::Support::Differ.new(
-
:object_preparer => lambda { |object| RSpec::Matchers::Composable.surface_descriptions_in(object) },
-
:color => RSpec::Matchers.configuration.color?
-
)
-
end
-
-
# Raises an RSpec::Expectations::ExpectationNotMetError with message.
-
# @param [String] message
-
# @param [Object] expected
-
# @param [Object] actual
-
#
-
# Adds a diff to the failure message when `expected` and `actual` are
-
# both present.
-
1
def fail_with(message, expected=nil, actual=nil)
-
unless message
-
raise ArgumentError, "Failure message is nil. Does your matcher define the " \
-
"appropriate failure_message[_when_negated] method to return a string?"
-
end
-
-
message = ::RSpec::Matchers::ExpectedsForMultipleDiffs.from(expected).message_with_diff(message, differ, actual)
-
-
RSpec::Support.notify_failure(RSpec::Expectations::ExpectationNotMetError.new message)
-
end
-
end
-
end
-
end
-
1
module RSpec
-
1
module Expectations
-
# @private
-
1
module ExpectationHelper
-
1
def self.check_message(msg)
-
9
unless msg.nil? || msg.respond_to?(:to_str) || msg.respond_to?(:call)
-
::Kernel.warn [
-
"WARNING: ignoring the provided expectation message argument (",
-
msg.inspect,
-
") since it is not a string or a proc."
-
].join
-
end
-
end
-
-
# Returns an RSpec-3+ compatible matcher, wrapping a legacy one
-
# in an adapter if necessary.
-
#
-
# @private
-
1
def self.modern_matcher_from(matcher)
-
LegacyMatcherAdapter::RSpec2.wrap(matcher) ||
-
9
LegacyMatcherAdapter::RSpec1.wrap(matcher) || matcher
-
end
-
-
1
def self.with_matcher(handler, matcher, message)
-
9
check_message(message)
-
9
matcher = modern_matcher_from(matcher)
-
9
yield matcher
-
ensure
-
9
::RSpec::Matchers.last_expectation_handler = handler
-
9
::RSpec::Matchers.last_matcher = matcher
-
end
-
-
1
def self.handle_failure(matcher, message, failure_message_method)
-
message = message.call if message.respond_to?(:call)
-
message ||= matcher.__send__(failure_message_method)
-
-
if matcher.respond_to?(:diffable?) && matcher.diffable?
-
::RSpec::Expectations.fail_with message, matcher.expected, matcher.actual
-
else
-
::RSpec::Expectations.fail_with message
-
end
-
end
-
end
-
-
# @private
-
1
class PositiveExpectationHandler
-
1
def self.handle_matcher(actual, initial_matcher, message=nil, &block)
-
9
ExpectationHelper.with_matcher(self, initial_matcher, message) do |matcher|
-
9
return ::RSpec::Matchers::BuiltIn::PositiveOperatorMatcher.new(actual) unless initial_matcher
-
9
matcher.matches?(actual, &block) || ExpectationHelper.handle_failure(matcher, message, :failure_message)
-
end
-
end
-
-
1
def self.verb
-
"should"
-
end
-
-
1
def self.should_method
-
:should
-
end
-
-
1
def self.opposite_should_method
-
:should_not
-
end
-
end
-
-
# @private
-
1
class NegativeExpectationHandler
-
1
def self.handle_matcher(actual, initial_matcher, message=nil, &block)
-
ExpectationHelper.with_matcher(self, initial_matcher, message) do |matcher|
-
return ::RSpec::Matchers::BuiltIn::NegativeOperatorMatcher.new(actual) unless initial_matcher
-
!(does_not_match?(matcher, actual, &block) || ExpectationHelper.handle_failure(matcher, message, :failure_message_when_negated))
-
end
-
end
-
-
1
def self.does_not_match?(matcher, actual, &block)
-
if matcher.respond_to?(:does_not_match?)
-
matcher.does_not_match?(actual, &block)
-
else
-
!matcher.matches?(actual, &block)
-
end
-
end
-
-
1
def self.verb
-
"should not"
-
end
-
-
1
def self.should_method
-
:should_not
-
end
-
-
1
def self.opposite_should_method
-
:should
-
end
-
end
-
-
# Wraps a matcher written against one of the legacy protocols in
-
# order to present the current protocol.
-
#
-
# @private
-
1
class LegacyMatcherAdapter < Matchers::MatcherDelegator
-
1
def initialize(matcher)
-
super
-
::RSpec.warn_deprecation(<<-EOS.gsub(/^\s+\|/, ''), :type => "legacy_matcher")
-
|#{matcher.class.name || matcher.inspect} implements a legacy RSpec matcher
-
|protocol. For the current protocol you should expose the failure messages
-
|via the `failure_message` and `failure_message_when_negated` methods.
-
|(Used from #{CallerFilter.first_non_rspec_line})
-
EOS
-
end
-
-
1
def self.wrap(matcher)
-
18
new(matcher) if interface_matches?(matcher)
-
end
-
-
# Starting in RSpec 1.2 (and continuing through all 2.x releases),
-
# the failure message protocol was:
-
# * `failure_message_for_should`
-
# * `failure_message_for_should_not`
-
# @private
-
1
class RSpec2 < self
-
1
def failure_message
-
base_matcher.failure_message_for_should
-
end
-
-
1
def failure_message_when_negated
-
base_matcher.failure_message_for_should_not
-
end
-
-
1
def self.interface_matches?(matcher)
-
(
-
!matcher.respond_to?(:failure_message) &&
-
9
matcher.respond_to?(:failure_message_for_should)
-
) || (
-
!matcher.respond_to?(:failure_message_when_negated) &&
-
9
matcher.respond_to?(:failure_message_for_should_not)
-
9
)
-
end
-
end
-
-
# Before RSpec 1.2, the failure message protocol was:
-
# * `failure_message`
-
# * `negative_failure_message`
-
# @private
-
1
class RSpec1 < self
-
1
def failure_message
-
base_matcher.failure_message
-
end
-
-
1
def failure_message_when_negated
-
base_matcher.negative_failure_message
-
end
-
-
# Note: `failure_message` is part of the RSpec 3 protocol
-
# (paired with `failure_message_when_negated`), so we don't check
-
# for `failure_message` here.
-
1
def self.interface_matches?(matcher)
-
!matcher.respond_to?(:failure_message_when_negated) &&
-
9
matcher.respond_to?(:negative_failure_message)
-
end
-
end
-
end
-
-
# RSpec 3.0 was released with the class name misspelled. For SemVer compatibility,
-
# we will provide this misspelled alias until 4.0.
-
# @deprecated Use LegacyMatcherAdapter instead.
-
# @private
-
1
LegacyMacherAdapter = LegacyMatcherAdapter
-
end
-
end
-
1
module RSpec
-
1
module Expectations
-
# @api private
-
# Provides methods for enabling and disabling the available
-
# syntaxes provided by rspec-expectations.
-
1
module Syntax
-
1
module_function
-
-
# @api private
-
# Determines where we add `should` and `should_not`.
-
1
def default_should_host
-
2
@default_should_host ||= ::Object.ancestors.last
-
end
-
-
# @api private
-
# Instructs rspec-expectations to warn on first usage of `should` or `should_not`.
-
# Enabled by default. This is largely here to facilitate testing.
-
1
def warn_about_should!
-
1
@warn_about_should = true
-
end
-
-
# @api private
-
# Generates a deprecation warning for the given method if no warning
-
# has already been issued.
-
1
def warn_about_should_unless_configured(method_name)
-
return unless @warn_about_should
-
-
RSpec.deprecate(
-
"Using `#{method_name}` from rspec-expectations' old `:should` syntax without explicitly enabling the syntax",
-
:replacement => "the new `:expect` syntax or explicitly enable `:should` with `config.expect_with(:rspec) { |c| c.syntax = :should }`"
-
)
-
-
@warn_about_should = false
-
end
-
-
# @api private
-
# Enables the `should` syntax.
-
1
def enable_should(syntax_host=default_should_host)
-
1
@warn_about_should = false if syntax_host == default_should_host
-
1
return if should_enabled?(syntax_host)
-
-
1
syntax_host.module_exec do
-
1
def should(matcher=nil, message=nil, &block)
-
::RSpec::Expectations::Syntax.warn_about_should_unless_configured(__method__)
-
::RSpec::Expectations::PositiveExpectationHandler.handle_matcher(self, matcher, message, &block)
-
end
-
-
1
def should_not(matcher=nil, message=nil, &block)
-
::RSpec::Expectations::Syntax.warn_about_should_unless_configured(__method__)
-
::RSpec::Expectations::NegativeExpectationHandler.handle_matcher(self, matcher, message, &block)
-
end
-
end
-
end
-
-
# @api private
-
# Disables the `should` syntax.
-
1
def disable_should(syntax_host=default_should_host)
-
return unless should_enabled?(syntax_host)
-
-
syntax_host.module_exec do
-
undef should
-
undef should_not
-
end
-
end
-
-
# @api private
-
# Enables the `expect` syntax.
-
1
def enable_expect(syntax_host=::RSpec::Matchers)
-
1
return if expect_enabled?(syntax_host)
-
-
1
syntax_host.module_exec do
-
1
def expect(value=::RSpec::Expectations::ExpectationTarget::UndefinedValue, &block)
-
9
::RSpec::Expectations::ExpectationTarget.for(value, block)
-
end
-
end
-
end
-
-
# @api private
-
# Disables the `expect` syntax.
-
1
def disable_expect(syntax_host=::RSpec::Matchers)
-
return unless expect_enabled?(syntax_host)
-
-
syntax_host.module_exec do
-
undef expect
-
end
-
end
-
-
# @api private
-
# Indicates whether or not the `should` syntax is enabled.
-
1
def should_enabled?(syntax_host=default_should_host)
-
1
syntax_host.method_defined?(:should)
-
end
-
-
# @api private
-
# Indicates whether or not the `expect` syntax is enabled.
-
1
def expect_enabled?(syntax_host=::RSpec::Matchers)
-
1
syntax_host.method_defined?(:expect)
-
end
-
end
-
end
-
end
-
-
1
if defined?(BasicObject)
-
# The legacy `:should` syntax adds the following methods directly to
-
# `BasicObject` so that they are available off of any object. Note, however,
-
# that this syntax does not always play nice with delegate/proxy objects.
-
# We recommend you use the non-monkeypatching `:expect` syntax instead.
-
1
class BasicObject
-
# @method should
-
# Passes if `matcher` returns true. Available on every `Object`.
-
# @example
-
# actual.should eq expected
-
# actual.should match /expression/
-
# @param [Matcher]
-
# matcher
-
# @param [String] message optional message to display when the expectation fails
-
# @return [Boolean] true if the expectation succeeds (else raises)
-
# @note This is only available when you have enabled the `:should` syntax.
-
# @see RSpec::Matchers
-
-
# @method should_not
-
# Passes if `matcher` returns false. Available on every `Object`.
-
# @example
-
# actual.should_not eq expected
-
# @param [Matcher]
-
# matcher
-
# @param [String] message optional message to display when the expectation fails
-
# @return [Boolean] false if the negative expectation succeeds (else raises)
-
# @note This is only available when you have enabled the `:should` syntax.
-
# @see RSpec::Matchers
-
end
-
end
-
1
module RSpec
-
1
module Expectations
-
# @private
-
1
module Version
-
1
STRING = '3.3.1'
-
end
-
end
-
end
-
1
require 'rspec/support'
-
1
RSpec::Support.require_rspec_support 'matcher_definition'
-
10
RSpec::Support.define_optimized_require_for_rspec(:matchers) { |f| require_relative(f) }
-
-
%w[
-
english_phrasing
-
composable
-
built_in
-
generated_descriptions
-
dsl
-
matcher_delegator
-
aliased_matcher
-
expecteds_for_multiple_diffs
-
9
].each { |file| RSpec::Support.require_rspec_matchers(file) }
-
-
# RSpec's top level namespace. All of rspec-expectations is contained
-
# in the `RSpec::Expectations` and `RSpec::Matchers` namespaces.
-
1
module RSpec
-
# RSpec::Matchers provides a number of useful matchers we use to define
-
# expectations. Any object that implements the [matcher protocol](Matchers/MatcherProtocol)
-
# can be used as a matcher.
-
#
-
# ## Predicates
-
#
-
# In addition to matchers that are defined explicitly, RSpec will create
-
# custom matchers on the fly for any arbitrary predicate, giving your specs a
-
# much more natural language feel.
-
#
-
# A Ruby predicate is a method that ends with a "?" and returns true or false.
-
# Common examples are `empty?`, `nil?`, and `instance_of?`.
-
#
-
# All you need to do is write `expect(..).to be_` followed by the predicate
-
# without the question mark, and RSpec will figure it out from there.
-
# For example:
-
#
-
# expect([]).to be_empty # => [].empty?() | passes
-
# expect([]).not_to be_empty # => [].empty?() | fails
-
#
-
# In addtion to prefixing the predicate matchers with "be_", you can also use "be_a_"
-
# and "be_an_", making your specs read much more naturally:
-
#
-
# expect("a string").to be_an_instance_of(String) # =>"a string".instance_of?(String) # passes
-
#
-
# expect(3).to be_a_kind_of(Fixnum) # => 3.kind_of?(Numeric) | passes
-
# expect(3).to be_a_kind_of(Numeric) # => 3.kind_of?(Numeric) | passes
-
# expect(3).to be_an_instance_of(Fixnum) # => 3.instance_of?(Fixnum) | passes
-
# expect(3).not_to be_an_instance_of(Numeric) # => 3.instance_of?(Numeric) | fails
-
#
-
# RSpec will also create custom matchers for predicates like `has_key?`. To
-
# use this feature, just state that the object should have_key(:key) and RSpec will
-
# call has_key?(:key) on the target. For example:
-
#
-
# expect(:a => "A").to have_key(:a)
-
# expect(:a => "A").to have_key(:b) # fails
-
#
-
# You can use this feature to invoke any predicate that begins with "has_", whether it is
-
# part of the Ruby libraries (like `Hash#has_key?`) or a method you wrote on your own class.
-
#
-
# Note that RSpec does not provide composable aliases for these dynamic predicate
-
# matchers. You can easily define your own aliases, though:
-
#
-
# RSpec::Matchers.alias_matcher :a_user_who_is_an_admin, :be_an_admin
-
# expect(user_list).to include(a_user_who_is_an_admin)
-
#
-
# ## Custom Matchers
-
#
-
# When you find that none of the stock matchers provide a natural feeling
-
# expectation, you can very easily write your own using RSpec's matcher DSL
-
# or writing one from scratch.
-
#
-
# ### Matcher DSL
-
#
-
# Imagine that you are writing a game in which players can be in various
-
# zones on a virtual board. To specify that bob should be in zone 4, you
-
# could say:
-
#
-
# expect(bob.current_zone).to eql(Zone.new("4"))
-
#
-
# But you might find it more expressive to say:
-
#
-
# expect(bob).to be_in_zone("4")
-
#
-
# and/or
-
#
-
# expect(bob).not_to be_in_zone("3")
-
#
-
# You can create such a matcher like so:
-
#
-
# RSpec::Matchers.define :be_in_zone do |zone|
-
# match do |player|
-
# player.in_zone?(zone)
-
# end
-
# end
-
#
-
# This will generate a <tt>be_in_zone</tt> method that returns a matcher
-
# with logical default messages for failures. You can override the failure
-
# messages and the generated description as follows:
-
#
-
# RSpec::Matchers.define :be_in_zone do |zone|
-
# match do |player|
-
# player.in_zone?(zone)
-
# end
-
#
-
# failure_message do |player|
-
# # generate and return the appropriate string.
-
# end
-
#
-
# failure_message_when_negated do |player|
-
# # generate and return the appropriate string.
-
# end
-
#
-
# description do
-
# # generate and return the appropriate string.
-
# end
-
# end
-
#
-
# Each of the message-generation methods has access to the block arguments
-
# passed to the <tt>create</tt> method (in this case, <tt>zone</tt>). The
-
# failure message methods (<tt>failure_message</tt> and
-
# <tt>failure_message_when_negated</tt>) are passed the actual value (the
-
# receiver of <tt>expect(..)</tt> or <tt>expect(..).not_to</tt>).
-
#
-
# ### Custom Matcher from scratch
-
#
-
# You could also write a custom matcher from scratch, as follows:
-
#
-
# class BeInZone
-
# def initialize(expected)
-
# @expected = expected
-
# end
-
#
-
# def matches?(target)
-
# @target = target
-
# @target.current_zone.eql?(Zone.new(@expected))
-
# end
-
#
-
# def failure_message
-
# "expected #{@target.inspect} to be in Zone #{@expected}"
-
# end
-
#
-
# def failure_message_when_negated
-
# "expected #{@target.inspect} not to be in Zone #{@expected}"
-
# end
-
# end
-
#
-
# ... and a method like this:
-
#
-
# def be_in_zone(expected)
-
# BeInZone.new(expected)
-
# end
-
#
-
# And then expose the method to your specs. This is normally done
-
# by including the method and the class in a module, which is then
-
# included in your spec:
-
#
-
# module CustomGameMatchers
-
# class BeInZone
-
# # ...
-
# end
-
#
-
# def be_in_zone(expected)
-
# # ...
-
# end
-
# end
-
#
-
# describe "Player behaviour" do
-
# include CustomGameMatchers
-
# # ...
-
# end
-
#
-
# or you can include in globally in a spec_helper.rb file <tt>require</tt>d
-
# from your spec file(s):
-
#
-
# RSpec::configure do |config|
-
# config.include(CustomGameMatchers)
-
# end
-
#
-
# ### Making custom matchers composable
-
#
-
# RSpec's built-in matchers are designed to be composed, in expressions like:
-
#
-
# expect(["barn", 2.45]).to contain_exactly(
-
# a_value_within(0.1).of(2.5),
-
# a_string_starting_with("bar")
-
# )
-
#
-
# Custom matchers can easily participate in composed matcher expressions like these.
-
# Include {RSpec::Matchers::Composable} in your custom matcher to make it support
-
# being composed (matchers defined using the DSL have this included automatically).
-
# Within your matcher's `matches?` method (or the `match` block, if using the DSL),
-
# use `values_match?(expected, actual)` rather than `expected == actual`.
-
# Under the covers, `values_match?` is able to match arbitrary
-
# nested data structures containing a mix of both matchers and non-matcher objects.
-
# It uses `===` and `==` to perform the matching, considering the values to
-
# match if either returns `true`. The `Composable` mixin also provides some helper
-
# methods for surfacing the matcher descriptions within your matcher's description
-
# or failure messages.
-
#
-
# RSpec's built-in matchers each have a number of aliases that rephrase the matcher
-
# from a verb phrase (such as `be_within`) to a noun phrase (such as `a_value_within`),
-
# which reads better when the matcher is passed as an argument in a composed matcher
-
# expressions, and also uses the noun-phrase wording in the matcher's `description`,
-
# for readable failure messages. You can alias your custom matchers in similar fashion
-
# using {RSpec::Matchers.alias_matcher}.
-
1
module Matchers
-
# @method expect
-
# Supports `expect(actual).to matcher` syntax by wrapping `actual` in an
-
# `ExpectationTarget`.
-
# @example
-
# expect(actual).to eq(expected)
-
# expect(actual).not_to eq(expected)
-
# @return [ExpectationTarget]
-
# @see ExpectationTarget#to
-
# @see ExpectationTarget#not_to
-
-
# Defines a matcher alias. The returned matcher's `description` will be overriden
-
# to reflect the phrasing of the new name, which will be used in failure messages
-
# when passed as an argument to another matcher in a composed matcher expression.
-
#
-
# @param new_name [Symbol] the new name for the matcher
-
# @param old_name [Symbol] the original name for the matcher
-
# @param options [Hash] options for the aliased matcher
-
# @option options [Class] :klass the ruby class to use as the decorator. (Not normally used).
-
# @yield [String] optional block that, when given, is used to define the overriden
-
# logic. The yielded arg is the original description or failure message. If no
-
# block is provided, a default override is used based on the old and new names.
-
#
-
# @example
-
# RSpec::Matchers.alias_matcher :a_list_that_sums_to, :sum_to
-
# sum_to(3).description # => "sum to 3"
-
# a_list_that_sums_to(3).description # => "a list that sums to 3"
-
#
-
# @example
-
# RSpec::Matchers.alias_matcher :a_list_sorted_by, :be_sorted_by do |description|
-
# description.sub("be sorted by", "a list sorted by")
-
# end
-
#
-
# be_sorted_by(:age).description # => "be sorted by age"
-
# a_list_sorted_by(:age).description # => "a list sorted by age"
-
#
-
# @!macro [attach] alias_matcher
-
# @!parse
-
# alias $1 $2
-
1
def self.alias_matcher(new_name, old_name, options={}, &description_override)
-
description_override ||= lambda do |old_desc|
-
old_desc.gsub(EnglishPhrasing.split_words(old_name), EnglishPhrasing.split_words(new_name))
-
57
end
-
113
klass = options.fetch(:klass) { AliasedMatcher }
-
-
57
define_method(new_name) do |*args, &block|
-
matcher = __send__(old_name, *args, &block)
-
klass.new(matcher, description_override)
-
end
-
end
-
-
# Defines a negated matcher. The returned matcher's `description` and `failure_message`
-
# will be overriden to reflect the phrasing of the new name, and the match logic will
-
# be based on the original matcher but negated.
-
#
-
# @param negated_name [Symbol] the name for the negated matcher
-
# @param base_name [Symbol] the name of the original matcher that will be negated
-
# @yield [String] optional block that, when given, is used to define the overriden
-
# logic. The yielded arg is the original description or failure message. If no
-
# block is provided, a default override is used based on the old and new names.
-
#
-
# @example
-
# RSpec::Matchers.define_negated_matcher :exclude, :include
-
# include(1, 2).description # => "include 1 and 2"
-
# exclude(1, 2).description # => "exclude 1 and 2"
-
#
-
# @note While the most obvious negated form may be to add a `not_` prefix,
-
# the failure messages you get with that form can be confusing (e.g.
-
# "expected [actual] to not [verb], but did not"). We've found it works
-
# best to find a more positive name for the negated form, such as
-
# `avoid_changing` rather than `not_change`.
-
1
def self.define_negated_matcher(negated_name, base_name, &description_override)
-
alias_matcher(negated_name, base_name, :klass => AliasedNegatedMatcher, &description_override)
-
end
-
-
# Allows multiple expectations in the provided block to fail, and then
-
# aggregates them into a single exception, rather than aborting on the
-
# first expectation failure like normal. This allows you to see all
-
# failures from an entire set of expectations without splitting each
-
# off into its own example (which may slow things down if the example
-
# setup is expensive).
-
#
-
# @param label [String] label for this aggregation block, which will be
-
# included in the aggregated exception message.
-
# @param metadata [Hash] additional metadata about this failure aggregation
-
# block. If multiple expectations fail, it will be exposed from the
-
# {Expectations::MultipleExpectationsNotMetError} exception. Mostly
-
# intended for internal RSpec use but you can use it as well.
-
# @yield Block containing as many expectation as you want. The block is
-
# simply yielded to, so you can trust that anything that works outside
-
# the block should work within it.
-
# @raise [Expectations::MultipleExpectationsNotMetError] raised when
-
# multiple expectations fail.
-
# @raise [Expectations::ExpectationNotMetError] raised when a single
-
# expectation fails.
-
# @raise [Exception] other sorts of exceptions will be raised as normal.
-
#
-
# @example
-
# aggregate_failures("verifying response") do
-
# expect(response.status).to eq(200)
-
# expect(response.headers).to include("Content-Type" => "text/plain")
-
# expect(response.body).to include("Success")
-
# end
-
#
-
# @note The implementation of this feature uses a thread-local variable,
-
# which means that if you have an expectation failure in another thread,
-
# it'll abort like normal.
-
1
def aggregate_failures(label=nil, metadata={}, &block)
-
Expectations::FailureAggregator.new(label, metadata).aggregate(&block)
-
end
-
-
# Passes if actual is truthy (anything but false or nil)
-
1
def be_truthy
-
BuiltIn::BeTruthy.new
-
end
-
1
alias_matcher :a_truthy_value, :be_truthy
-
-
# Passes if actual is falsey (false or nil)
-
1
def be_falsey
-
BuiltIn::BeFalsey.new
-
end
-
1
alias_matcher :be_falsy, :be_falsey
-
1
alias_matcher :a_falsey_value, :be_falsey
-
1
alias_matcher :a_falsy_value, :be_falsey
-
-
# Passes if actual is nil
-
1
def be_nil
-
BuiltIn::BeNil.new
-
end
-
1
alias_matcher :a_nil_value, :be_nil
-
-
# @example
-
# expect(actual).to be_truthy
-
# expect(actual).to be_falsey
-
# expect(actual).to be_nil
-
# expect(actual).to be_[arbitrary_predicate](*args)
-
# expect(actual).not_to be_nil
-
# expect(actual).not_to be_[arbitrary_predicate](*args)
-
#
-
# Given true, false, or nil, will pass if actual value is true, false or
-
# nil (respectively). Given no args means the caller should satisfy an if
-
# condition (to be or not to be).
-
#
-
# Predicates are any Ruby method that ends in a "?" and returns true or
-
# false. Given be_ followed by arbitrary_predicate (without the "?"),
-
# RSpec will match convert that into a query against the target object.
-
#
-
# The arbitrary_predicate feature will handle any predicate prefixed with
-
# "be_an_" (e.g. be_an_instance_of), "be_a_" (e.g. be_a_kind_of) or "be_"
-
# (e.g. be_empty), letting you choose the prefix that best suits the
-
# predicate.
-
1
def be(*args)
-
args.empty? ? Matchers::BuiltIn::Be.new : equal(*args)
-
end
-
1
alias_matcher :a_value, :be, :klass => AliasedMatcherWithOperatorSupport
-
-
# passes if target.kind_of?(klass)
-
1
def be_a(klass)
-
be_a_kind_of(klass)
-
end
-
1
alias_method :be_an, :be_a
-
-
# Passes if actual.instance_of?(expected)
-
#
-
# @example
-
# expect(5).to be_an_instance_of(Fixnum)
-
# expect(5).not_to be_an_instance_of(Numeric)
-
# expect(5).not_to be_an_instance_of(Float)
-
1
def be_an_instance_of(expected)
-
BuiltIn::BeAnInstanceOf.new(expected)
-
end
-
1
alias_method :be_instance_of, :be_an_instance_of
-
1
alias_matcher :an_instance_of, :be_an_instance_of
-
-
# Passes if actual.kind_of?(expected)
-
#
-
# @example
-
# expect(5).to be_a_kind_of(Fixnum)
-
# expect(5).to be_a_kind_of(Numeric)
-
# expect(5).not_to be_a_kind_of(Float)
-
1
def be_a_kind_of(expected)
-
BuiltIn::BeAKindOf.new(expected)
-
end
-
1
alias_method :be_kind_of, :be_a_kind_of
-
1
alias_matcher :a_kind_of, :be_a_kind_of
-
-
# Passes if actual.between?(min, max). Works with any Comparable object,
-
# including String, Symbol, Time, or Numeric (Fixnum, Bignum, Integer,
-
# Float, Complex, and Rational).
-
#
-
# By default, `be_between` is inclusive (i.e. passes when given either the max or min value),
-
# but you can make it `exclusive` by chaining that off the matcher.
-
#
-
# @example
-
# expect(5).to be_between(1, 10)
-
# expect(11).not_to be_between(1, 10)
-
# expect(10).not_to be_between(1, 10).exclusive
-
1
def be_between(min, max)
-
BuiltIn::BeBetween.new(min, max)
-
end
-
1
alias_matcher :a_value_between, :be_between
-
-
# Passes if actual == expected +/- delta
-
#
-
# @example
-
# expect(result).to be_within(0.5).of(3.0)
-
# expect(result).not_to be_within(0.5).of(3.0)
-
1
def be_within(delta)
-
BuiltIn::BeWithin.new(delta)
-
end
-
1
alias_matcher :a_value_within, :be_within
-
1
alias_matcher :within, :be_within
-
-
# Applied to a proc, specifies that its execution will cause some value to
-
# change.
-
#
-
# @param [Object] receiver
-
# @param [Symbol] message the message to send the receiver
-
#
-
# You can either pass <tt>receiver</tt> and <tt>message</tt>, or a block,
-
# but not both.
-
#
-
# When passing a block, it must use the `{ ... }` format, not
-
# do/end, as `{ ... }` binds to the `change` method, whereas do/end
-
# would errantly bind to the `expect(..).to` or `expect(...).not_to` method.
-
#
-
# You can chain any of the following off of the end to specify details
-
# about the change:
-
#
-
# * `from`
-
# * `to`
-
#
-
# or any one of:
-
#
-
# * `by`
-
# * `by_at_least`
-
# * `by_at_most`
-
#
-
# @example
-
# expect {
-
# team.add_player(player)
-
# }.to change(roster, :count)
-
#
-
# expect {
-
# team.add_player(player)
-
# }.to change(roster, :count).by(1)
-
#
-
# expect {
-
# team.add_player(player)
-
# }.to change(roster, :count).by_at_least(1)
-
#
-
# expect {
-
# team.add_player(player)
-
# }.to change(roster, :count).by_at_most(1)
-
#
-
# string = "string"
-
# expect {
-
# string.reverse!
-
# }.to change { string }.from("string").to("gnirts")
-
#
-
# string = "string"
-
# expect {
-
# string
-
# }.not_to change { string }.from("string")
-
#
-
# expect {
-
# person.happy_birthday
-
# }.to change(person, :birthday).from(32).to(33)
-
#
-
# expect {
-
# employee.develop_great_new_social_networking_app
-
# }.to change(employee, :title).from("Mail Clerk").to("CEO")
-
#
-
# expect {
-
# doctor.leave_office
-
# }.to change(doctor, :sign).from(/is in/).to(/is out/)
-
#
-
# user = User.new(:type => "admin")
-
# expect {
-
# user.symbolize_type
-
# }.to change(user, :type).from(String).to(Symbol)
-
#
-
# == Notes
-
#
-
# Evaluates `receiver.message` or `block` before and after it
-
# evaluates the block passed to `expect`.
-
#
-
# `expect( ... ).not_to change` supports the form that specifies `from`
-
# (which specifies what you expect the starting, unchanged value to be)
-
# but does not support forms with subsequent calls to `by`, `by_at_least`,
-
# `by_at_most` or `to`.
-
1
def change(receiver=nil, message=nil, &block)
-
BuiltIn::Change.new(receiver, message, &block)
-
end
-
1
alias_matcher :a_block_changing, :change
-
1
alias_matcher :changing, :change
-
-
# Passes if actual contains all of the expected regardless of order.
-
# This works for collections. Pass in multiple args and it will only
-
# pass if all args are found in collection.
-
#
-
# @note This is also available using the `=~` operator with `should`,
-
# but `=~` is not supported with `expect`.
-
#
-
# @example
-
# expect([1, 2, 3]).to contain_exactly(1, 2, 3)
-
# expect([1, 2, 3]).to contain_exactly(1, 3, 2)
-
#
-
# @see #match_array
-
1
def contain_exactly(*items)
-
36
BuiltIn::ContainExactly.new(items)
-
end
-
1
alias_matcher :a_collection_containing_exactly, :contain_exactly
-
1
alias_matcher :containing_exactly, :contain_exactly
-
-
# Passes if actual covers expected. This works for
-
# Ranges. You can also pass in multiple args
-
# and it will only pass if all args are found in Range.
-
#
-
# @example
-
# expect(1..10).to cover(5)
-
# expect(1..10).to cover(4, 6)
-
# expect(1..10).to cover(4, 6, 11) # fails
-
# expect(1..10).not_to cover(11)
-
# expect(1..10).not_to cover(5) # fails
-
#
-
# ### Warning:: Ruby >= 1.9 only
-
1
def cover(*values)
-
BuiltIn::Cover.new(*values)
-
end
-
1
alias_matcher :a_range_covering, :cover
-
1
alias_matcher :covering, :cover
-
-
# Matches if the actual value ends with the expected value(s). In the case
-
# of a string, matches against the last `expected.length` characters of the
-
# actual string. In the case of an array, matches against the last
-
# `expected.length` elements of the actual array.
-
#
-
# @example
-
# expect("this string").to end_with "string"
-
# expect([0, 1, 2, 3, 4]).to end_with 4
-
# expect([0, 2, 3, 4, 4]).to end_with 3, 4
-
1
def end_with(*expected)
-
BuiltIn::EndWith.new(*expected)
-
end
-
1
alias_matcher :a_collection_ending_with, :end_with
-
1
alias_matcher :a_string_ending_with, :end_with
-
1
alias_matcher :ending_with, :end_with
-
-
# Passes if <tt>actual == expected</tt>.
-
#
-
# See http://www.ruby-doc.org/core/classes/Object.html#M001057 for more
-
# information about equality in Ruby.
-
#
-
# @example
-
# expect(5).to eq(5)
-
# expect(5).not_to eq(3)
-
1
def eq(expected)
-
1
BuiltIn::Eq.new(expected)
-
end
-
1
alias_matcher :an_object_eq_to, :eq
-
1
alias_matcher :eq_to, :eq
-
-
# Passes if `actual.eql?(expected)`
-
#
-
# See http://www.ruby-doc.org/core/classes/Object.html#M001057 for more
-
# information about equality in Ruby.
-
#
-
# @example
-
# expect(5).to eql(5)
-
# expect(5).not_to eql(3)
-
1
def eql(expected)
-
BuiltIn::Eql.new(expected)
-
end
-
1
alias_matcher :an_object_eql_to, :eql
-
1
alias_matcher :eql_to, :eql
-
-
# Passes if <tt>actual.equal?(expected)</tt> (object identity).
-
#
-
# See http://www.ruby-doc.org/core/classes/Object.html#M001057 for more
-
# information about equality in Ruby.
-
#
-
# @example
-
# expect(5).to equal(5) # Fixnums are equal
-
# expect("5").not_to equal("5") # Strings that look the same are not the same object
-
1
def equal(expected)
-
BuiltIn::Equal.new(expected)
-
end
-
1
alias_matcher :an_object_equal_to, :equal
-
1
alias_matcher :equal_to, :equal
-
-
# Passes if `actual.exist?` or `actual.exists?`
-
#
-
# @example
-
# expect(File).to exist("path/to/file")
-
1
def exist(*args)
-
BuiltIn::Exist.new(*args)
-
end
-
1
alias_matcher :an_object_existing, :exist
-
1
alias_matcher :existing, :exist
-
-
# Passes if actual's attribute values match the expected attributes hash.
-
# This works no matter how you define your attribute readers.
-
#
-
# @example
-
# Person = Struct.new(:name, :age)
-
# person = Person.new("Bob", 32)
-
#
-
# expect(person).to have_attributes(:name => "Bob", :age => 32)
-
# expect(person).to have_attributes(:name => a_string_starting_with("B"), :age => (a_value > 30) )
-
#
-
# @note It will fail if actual doesn't respond to any of the expected attributes.
-
#
-
# @example
-
# expect(person).to have_attributes(:color => "red")
-
1
def have_attributes(expected)
-
BuiltIn::HaveAttributes.new(expected)
-
end
-
1
alias_matcher :an_object_having_attributes, :have_attributes
-
-
# Passes if actual includes expected. This works for
-
# collections and Strings. You can also pass in multiple args
-
# and it will only pass if all args are found in collection.
-
#
-
# @example
-
# expect([1,2,3]).to include(3)
-
# expect([1,2,3]).to include(2,3)
-
# expect([1,2,3]).to include(2,3,4) # fails
-
# expect([1,2,3]).not_to include(4)
-
# expect("spread").to include("read")
-
# expect("spread").not_to include("red")
-
# expect(:a => 1, :b => 2).to include(:a)
-
# expect(:a => 1, :b => 2).to include(:a, :b)
-
# expect(:a => 1, :b => 2).to include(:a => 1)
-
# expect(:a => 1, :b => 2).to include(:b => 2, :a => 1)
-
# expect(:a => 1, :b => 2).to include(:c) # fails
-
# expect(:a => 1, :b => 2).not_to include(:a => 2)
-
1
def include(*expected)
-
BuiltIn::Include.new(*expected)
-
end
-
1
alias_matcher :a_collection_including, :include
-
1
alias_matcher :a_string_including, :include
-
1
alias_matcher :a_hash_including, :include
-
1
alias_matcher :including, :include
-
-
# Passes if the provided matcher passes when checked against all
-
# elements of the collection.
-
#
-
# @example
-
# expect([1, 3, 5]).to all be_odd
-
# expect([1, 3, 6]).to all be_odd # fails
-
#
-
# @note The negative form `not_to all` is not supported. Instead
-
# use `not_to include` or pass a negative form of a matcher
-
# as the argument (e.g. `all exclude(:foo)`).
-
#
-
# @note You can also use this with compound matchers as well.
-
#
-
# @example
-
# expect([1, 3, 5]).to all( be_odd.and be_an(Integer) )
-
1
def all(expected)
-
BuiltIn::All.new(expected)
-
end
-
-
# Given a `Regexp` or `String`, passes if `actual.match(pattern)`
-
# Given an arbitrary nested data structure (e.g. arrays and hashes),
-
# matches if `expected === actual` || `actual == expected` for each
-
# pair of elements.
-
#
-
# @example
-
# expect(email).to match(/^([^\s]+)((?:[-a-z0-9]+\.)+[a-z]{2,})$/i)
-
# expect(email).to match("@example.com")
-
#
-
# @example
-
# hash = {
-
# :a => {
-
# :b => ["foo", 5],
-
# :c => { :d => 2.05 }
-
# }
-
# }
-
#
-
# expect(hash).to match(
-
# :a => {
-
# :b => a_collection_containing_exactly(
-
# a_string_starting_with("f"),
-
# an_instance_of(Fixnum)
-
# ),
-
# :c => { :d => (a_value < 3) }
-
# }
-
# )
-
#
-
# @note The `match_regex` alias is deprecated and is not recommended for use.
-
# It was added in 2.12.1 to facilitate its use from within custom
-
# matchers (due to how the custom matcher DSL was evaluated in 2.x,
-
# `match` could not be used there), but is no longer needed in 3.x.
-
1
def match(expected)
-
BuiltIn::Match.new(expected)
-
end
-
1
alias_matcher :match_regex, :match
-
1
alias_matcher :an_object_matching, :match
-
1
alias_matcher :a_string_matching, :match
-
1
alias_matcher :matching, :match
-
-
# An alternate form of `contain_exactly` that accepts
-
# the expected contents as a single array arg rather
-
# that splatted out as individual items.
-
#
-
# @example
-
# expect(results).to contain_exactly(1, 2)
-
# # is identical to:
-
# expect(results).to match_array([1, 2])
-
#
-
# @see #contain_exactly
-
1
def match_array(items)
-
36
contain_exactly(*items)
-
end
-
-
# With no arg, passes if the block outputs `to_stdout` or `to_stderr`.
-
# With a string, passes if the block outputs that specific string `to_stdout` or `to_stderr`.
-
# With a regexp or matcher, passes if the block outputs a string `to_stdout` or `to_stderr` that matches.
-
#
-
# To capture output from any spawned subprocess as well, use `to_stdout_from_any_process` or
-
# `to_stderr_from_any_process`. Output from any process that inherits the main process's corresponding
-
# standard stream will be captured.
-
#
-
# @example
-
# expect { print 'foo' }.to output.to_stdout
-
# expect { print 'foo' }.to output('foo').to_stdout
-
# expect { print 'foo' }.to output(/foo/).to_stdout
-
#
-
# expect { do_something }.to_not output.to_stdout
-
#
-
# expect { warn('foo') }.to output.to_stderr
-
# expect { warn('foo') }.to output('foo').to_stderr
-
# expect { warn('foo') }.to output(/foo/).to_stderr
-
#
-
# expect { do_something }.to_not output.to_stderr
-
#
-
# expect { system('echo foo') }.to output("foo\n").to_stdout_from_any_process
-
# expect { system('echo foo', out: :err) }.to output("foo\n").to_stderr_from_any_process
-
#
-
# @note `to_stdout` and `to_stderr` work by temporarily replacing `$stdout` or `$stderr`,
-
# so they're not able to intercept stream output that explicitly uses `STDOUT`/`STDERR`
-
# or that uses a reference to `$stdout`/`$stderr` that was stored before the
-
# matcher was used.
-
# @note `to_stdout_from_any_process` and `to_stderr_from_any_process` use Tempfiles, and
-
# are thus significantly (~30x) slower than `to_stdout` and `to_stderr`.
-
1
def output(expected=nil)
-
BuiltIn::Output.new(expected)
-
end
-
1
alias_matcher :a_block_outputting, :output
-
-
# With no args, matches if any error is raised.
-
# With a named error, matches only if that specific error is raised.
-
# With a named error and messsage specified as a String, matches only if both match.
-
# With a named error and messsage specified as a Regexp, matches only if both match.
-
# Pass an optional block to perform extra verifications on the exception matched
-
#
-
# @example
-
# expect { do_something_risky }.to raise_error
-
# expect { do_something_risky }.to raise_error(PoorRiskDecisionError)
-
# expect { do_something_risky }.to raise_error(PoorRiskDecisionError) { |error| expect(error.data).to eq 42 }
-
# expect { do_something_risky }.to raise_error(PoorRiskDecisionError, "that was too risky")
-
# expect { do_something_risky }.to raise_error(PoorRiskDecisionError, /oo ri/)
-
#
-
# expect { do_something_risky }.not_to raise_error
-
1
def raise_error(error=nil, message=nil, &block)
-
BuiltIn::RaiseError.new(error, message, &block)
-
end
-
1
alias_method :raise_exception, :raise_error
-
-
1
alias_matcher :a_block_raising, :raise_error do |desc|
-
desc.sub("raise", "a block raising")
-
end
-
-
1
alias_matcher :raising, :raise_error do |desc|
-
desc.sub("raise", "raising")
-
end
-
-
# Matches if the target object responds to all of the names
-
# provided. Names can be Strings or Symbols.
-
#
-
# @example
-
# expect("string").to respond_to(:length)
-
#
-
1
def respond_to(*names)
-
BuiltIn::RespondTo.new(*names)
-
end
-
1
alias_matcher :an_object_responding_to, :respond_to
-
1
alias_matcher :responding_to, :respond_to
-
-
# Passes if the submitted block returns true. Yields target to the
-
# block.
-
#
-
# Generally speaking, this should be thought of as a last resort when
-
# you can't find any other way to specify the behaviour you wish to
-
# specify.
-
#
-
# If you do find yourself in such a situation, you could always write
-
# a custom matcher, which would likely make your specs more expressive.
-
#
-
# @param description [String] optional description to be used for this matcher.
-
#
-
# @example
-
# expect(5).to satisfy { |n| n > 3 }
-
# expect(5).to satisfy("be greater than 3") { |n| n > 3 }
-
1
def satisfy(description="satisfy block", &block)
-
BuiltIn::Satisfy.new(description, &block)
-
end
-
1
alias_matcher :an_object_satisfying, :satisfy
-
1
alias_matcher :satisfying, :satisfy
-
-
# Matches if the actual value starts with the expected value(s). In the
-
# case of a string, matches against the first `expected.length` characters
-
# of the actual string. In the case of an array, matches against the first
-
# `expected.length` elements of the actual array.
-
#
-
# @example
-
# expect("this string").to start_with "this s"
-
# expect([0, 1, 2, 3, 4]).to start_with 0
-
# expect([0, 2, 3, 4, 4]).to start_with 0, 1
-
1
def start_with(*expected)
-
BuiltIn::StartWith.new(*expected)
-
end
-
1
alias_matcher :a_collection_starting_with, :start_with
-
1
alias_matcher :a_string_starting_with, :start_with
-
1
alias_matcher :starting_with, :start_with
-
-
# Given no argument, matches if a proc throws any Symbol.
-
#
-
# Given a Symbol, matches if the given proc throws the specified Symbol.
-
#
-
# Given a Symbol and an arg, matches if the given proc throws the
-
# specified Symbol with the specified arg.
-
#
-
# @example
-
# expect { do_something_risky }.to throw_symbol
-
# expect { do_something_risky }.to throw_symbol(:that_was_risky)
-
# expect { do_something_risky }.to throw_symbol(:that_was_risky, 'culprit')
-
#
-
# expect { do_something_risky }.not_to throw_symbol
-
# expect { do_something_risky }.not_to throw_symbol(:that_was_risky)
-
# expect { do_something_risky }.not_to throw_symbol(:that_was_risky, 'culprit')
-
1
def throw_symbol(expected_symbol=nil, expected_arg=nil)
-
BuiltIn::ThrowSymbol.new(expected_symbol, expected_arg)
-
end
-
-
1
alias_matcher :a_block_throwing, :throw_symbol do |desc|
-
desc.sub("throw", "a block throwing")
-
end
-
-
1
alias_matcher :throwing, :throw_symbol do |desc|
-
desc.sub("throw", "throwing")
-
end
-
-
# Passes if the method called in the expect block yields, regardless
-
# of whether or not arguments are yielded.
-
#
-
# @example
-
# expect { |b| 5.tap(&b) }.to yield_control
-
# expect { |b| "a".to_sym(&b) }.not_to yield_control
-
#
-
# @note Your expect block must accept a parameter and pass it on to
-
# the method-under-test as a block.
-
1
def yield_control
-
BuiltIn::YieldControl.new
-
end
-
1
alias_matcher :a_block_yielding_control, :yield_control
-
1
alias_matcher :yielding_control, :yield_control
-
-
# Passes if the method called in the expect block yields with
-
# no arguments. Fails if it does not yield, or yields with arguments.
-
#
-
# @example
-
# expect { |b| User.transaction(&b) }.to yield_with_no_args
-
# expect { |b| 5.tap(&b) }.not_to yield_with_no_args # because it yields with `5`
-
# expect { |b| "a".to_sym(&b) }.not_to yield_with_no_args # because it does not yield
-
#
-
# @note Your expect block must accept a parameter and pass it on to
-
# the method-under-test as a block.
-
# @note This matcher is not designed for use with methods that yield
-
# multiple times.
-
1
def yield_with_no_args
-
BuiltIn::YieldWithNoArgs.new
-
end
-
1
alias_matcher :a_block_yielding_with_no_args, :yield_with_no_args
-
1
alias_matcher :yielding_with_no_args, :yield_with_no_args
-
-
# Given no arguments, matches if the method called in the expect
-
# block yields with arguments (regardless of what they are or how
-
# many there are).
-
#
-
# Given arguments, matches if the method called in the expect block
-
# yields with arguments that match the given arguments.
-
#
-
# Argument matching is done using `===` (the case match operator)
-
# and `==`. If the expected and actual arguments match with either
-
# operator, the matcher will pass.
-
#
-
# @example
-
# expect { |b| 5.tap(&b) }.to yield_with_args # because #tap yields an arg
-
# expect { |b| 5.tap(&b) }.to yield_with_args(5) # because 5 == 5
-
# expect { |b| 5.tap(&b) }.to yield_with_args(Fixnum) # because Fixnum === 5
-
# expect { |b| File.open("f.txt", &b) }.to yield_with_args(/txt/) # because /txt/ === "f.txt"
-
#
-
# expect { |b| User.transaction(&b) }.not_to yield_with_args # because it yields no args
-
# expect { |b| 5.tap(&b) }.not_to yield_with_args(1, 2, 3)
-
#
-
# @note Your expect block must accept a parameter and pass it on to
-
# the method-under-test as a block.
-
# @note This matcher is not designed for use with methods that yield
-
# multiple times.
-
1
def yield_with_args(*args)
-
BuiltIn::YieldWithArgs.new(*args)
-
end
-
1
alias_matcher :a_block_yielding_with_args, :yield_with_args
-
1
alias_matcher :yielding_with_args, :yield_with_args
-
-
# Designed for use with methods that repeatedly yield (such as
-
# iterators). Passes if the method called in the expect block yields
-
# multiple times with arguments matching those given.
-
#
-
# Argument matching is done using `===` (the case match operator)
-
# and `==`. If the expected and actual arguments match with either
-
# operator, the matcher will pass.
-
#
-
# @example
-
# expect { |b| [1, 2, 3].each(&b) }.to yield_successive_args(1, 2, 3)
-
# expect { |b| { :a => 1, :b => 2 }.each(&b) }.to yield_successive_args([:a, 1], [:b, 2])
-
# expect { |b| [1, 2, 3].each(&b) }.not_to yield_successive_args(1, 2)
-
#
-
# @note Your expect block must accept a parameter and pass it on to
-
# the method-under-test as a block.
-
1
def yield_successive_args(*args)
-
BuiltIn::YieldSuccessiveArgs.new(*args)
-
end
-
1
alias_matcher :a_block_yielding_successive_args, :yield_successive_args
-
1
alias_matcher :yielding_successive_args, :yield_successive_args
-
-
# Delegates to {RSpec::Expectations.configuration}.
-
# This is here because rspec-core's `expect_with` option
-
# looks for a `configuration` method on the mixin
-
# (`RSpec::Matchers`) to yield to a block.
-
# @return [RSpec::Expectations::Configuration] the configuration object
-
1
def self.configuration
-
Expectations.configuration
-
end
-
-
1
private
-
-
1
BE_PREDICATE_REGEX = /^(be_(?:an?_)?)(.*)/
-
1
HAS_REGEX = /^(?:have_)(.*)/
-
1
DYNAMIC_MATCHER_REGEX = Regexp.union(BE_PREDICATE_REGEX, HAS_REGEX)
-
-
1
def method_missing(method, *args, &block)
-
case method.to_s
-
when BE_PREDICATE_REGEX
-
BuiltIn::BePredicate.new(method, *args, &block)
-
when HAS_REGEX
-
BuiltIn::Has.new(method, *args, &block)
-
else
-
super
-
end
-
end
-
-
1
if RUBY_VERSION.to_f >= 1.9
-
1
def respond_to_missing?(method, *)
-
method =~ DYNAMIC_MATCHER_REGEX || super
-
end
-
else # for 1.8.7
-
# :nocov:
-
skipped
def respond_to?(method, *)
-
skipped
method = method.to_s
-
skipped
method =~ DYNAMIC_MATCHER_REGEX || super
-
skipped
end
-
skipped
public :respond_to?
-
# :nocov:
-
end
-
-
# @api private
-
1
def self.is_a_matcher?(obj)
-
396
return true if ::RSpec::Matchers::BuiltIn::BaseMatcher === obj
-
396
begin
-
396
return false if obj.respond_to?(:i_respond_to_everything_so_im_not_really_a_matcher)
-
rescue NoMethodError
-
# Some objects, like BasicObject, don't implemented standard
-
# reflection methods.
-
return false
-
end
-
396
return false unless obj.respond_to?(:matches?)
-
-
obj.respond_to?(:failure_message) ||
-
obj.respond_to?(:failure_message_for_should) # support legacy matchers
-
end
-
-
1
::RSpec::Support.register_matcher_definition do |obj|
-
is_a_matcher?(obj)
-
end
-
-
# @api private
-
1
def self.is_a_describable_matcher?(obj)
-
is_a_matcher?(obj) && obj.respond_to?(:description)
-
end
-
end
-
end
-
1
module RSpec
-
1
module Matchers
-
# Decorator that wraps a matcher and overrides `description`
-
# using the provided block in order to support an alias
-
# of a matcher. This is intended for use when composing
-
# matchers, so that you can use an expression like
-
# `include( a_value_within(0.1).of(3) )` rather than
-
# `include( be_within(0.1).of(3) )`, and have the corresponding
-
# description read naturally.
-
#
-
# @api private
-
1
class AliasedMatcher < MatcherDelegator
-
1
def initialize(base_matcher, description_block)
-
@description_block = description_block
-
super(base_matcher)
-
end
-
-
# Forward messages on to the wrapped matcher.
-
# Since many matchers provide a fluent interface
-
# (e.g. `a_value_within(0.1).of(3)`), we need to wrap
-
# the returned value if it responds to `description`,
-
# so that our override can be applied when it is eventually
-
# used.
-
1
def method_missing(*)
-
return_val = super
-
return return_val unless RSpec::Matchers.is_a_matcher?(return_val)
-
self.class.new(return_val, @description_block)
-
end
-
-
# Provides the description of the aliased matcher. Aliased matchers
-
# are designed to behave identically to the original matcher except
-
# for the description and failure messages. The description is different
-
# to reflect the aliased name.
-
#
-
# @api private
-
1
def description
-
@description_block.call(super)
-
end
-
-
# Provides the failure_message of the aliased matcher. Aliased matchers
-
# are designed to behave identically to the original matcher except
-
# for the description and failure messages. The failure_message is different
-
# to reflect the aliased name.
-
#
-
# @api private
-
1
def failure_message
-
@description_block.call(super)
-
end
-
-
# Provides the failure_message_when_negated of the aliased matcher. Aliased matchers
-
# are designed to behave identically to the original matcher except
-
# for the description and failure messages. The failure_message_when_negated is different
-
# to reflect the aliased name.
-
#
-
# @api private
-
1
def failure_message_when_negated
-
@description_block.call(super)
-
end
-
end
-
-
# Decorator used for matchers that have special implementations of
-
# operators like `==` and `===`.
-
# @private
-
1
class AliasedMatcherWithOperatorSupport < AliasedMatcher
-
# We undef these so that they get delegated via `method_missing`.
-
1
undef ==
-
1
undef ===
-
end
-
-
# @private
-
1
class AliasedNegatedMatcher < AliasedMatcher
-
1
def matches?(*args, &block)
-
if @base_matcher.respond_to?(:does_not_match?)
-
@base_matcher.does_not_match?(*args, &block)
-
else
-
!super
-
end
-
end
-
-
1
def does_not_match?(*args, &block)
-
@base_matcher.matches?(*args, &block)
-
end
-
-
1
def failure_message
-
optimal_failure_message(__method__, :failure_message_when_negated)
-
end
-
-
1
def failure_message_when_negated
-
optimal_failure_message(__method__, :failure_message)
-
end
-
-
1
private
-
-
1
DefaultFailureMessages = BuiltIn::BaseMatcher::DefaultFailureMessages
-
-
# For a matcher that uses the default failure messages, we prefer to
-
# use the override provided by the `description_block`, because it
-
# includes the phrasing that the user has expressed a preference for
-
# by going through the effort of defining a negated matcher.
-
#
-
# However, if the override didn't actually change anything, then we
-
# should return the opposite failure message instead -- the overriden
-
# message is going to be confusing if we return it as-is, as it represents
-
# the non-negated failure message for a negated match (or vice versa).
-
1
def optimal_failure_message(same, inverted)
-
if DefaultFailureMessages.has_default_failure_messages?(@base_matcher)
-
base_message = @base_matcher.__send__(same)
-
overriden = @description_block.call(base_message)
-
return overriden if overriden != base_message
-
end
-
-
@base_matcher.__send__(inverted)
-
end
-
end
-
end
-
end
-
1
RSpec::Support.require_rspec_matchers "built_in/base_matcher"
-
-
1
module RSpec
-
1
module Matchers
-
# Container module for all built-in matchers. The matcher classes are here
-
# (rather than directly under `RSpec::Matchers`) in order to prevent name
-
# collisions, since `RSpec::Matchers` gets included into the user's namespace.
-
#
-
# Autoloading is used to delay when the matcher classes get loaded, allowing
-
# rspec-matchers to boot faster, and avoiding loading matchers the user is
-
# not using.
-
1
module BuiltIn
-
1
autoload :BeAKindOf, 'rspec/matchers/built_in/be_kind_of'
-
1
autoload :BeAnInstanceOf, 'rspec/matchers/built_in/be_instance_of'
-
1
autoload :BeBetween, 'rspec/matchers/built_in/be_between'
-
1
autoload :Be, 'rspec/matchers/built_in/be'
-
1
autoload :BeComparedTo, 'rspec/matchers/built_in/be'
-
1
autoload :BeFalsey, 'rspec/matchers/built_in/be'
-
1
autoload :BeNil, 'rspec/matchers/built_in/be'
-
1
autoload :BePredicate, 'rspec/matchers/built_in/be'
-
1
autoload :BeTruthy, 'rspec/matchers/built_in/be'
-
1
autoload :BeWithin, 'rspec/matchers/built_in/be_within'
-
1
autoload :Change, 'rspec/matchers/built_in/change'
-
1
autoload :Compound, 'rspec/matchers/built_in/compound'
-
1
autoload :ContainExactly, 'rspec/matchers/built_in/contain_exactly'
-
1
autoload :Cover, 'rspec/matchers/built_in/cover'
-
1
autoload :EndWith, 'rspec/matchers/built_in/start_or_end_with'
-
1
autoload :Eq, 'rspec/matchers/built_in/eq'
-
1
autoload :Eql, 'rspec/matchers/built_in/eql'
-
1
autoload :Equal, 'rspec/matchers/built_in/equal'
-
1
autoload :Exist, 'rspec/matchers/built_in/exist'
-
1
autoload :Has, 'rspec/matchers/built_in/has'
-
1
autoload :HaveAttributes, 'rspec/matchers/built_in/have_attributes'
-
1
autoload :Include, 'rspec/matchers/built_in/include'
-
1
autoload :All, 'rspec/matchers/built_in/all'
-
1
autoload :Match, 'rspec/matchers/built_in/match'
-
1
autoload :NegativeOperatorMatcher, 'rspec/matchers/built_in/operators'
-
1
autoload :OperatorMatcher, 'rspec/matchers/built_in/operators'
-
1
autoload :Output, 'rspec/matchers/built_in/output'
-
1
autoload :PositiveOperatorMatcher, 'rspec/matchers/built_in/operators'
-
1
autoload :RaiseError, 'rspec/matchers/built_in/raise_error'
-
1
autoload :RespondTo, 'rspec/matchers/built_in/respond_to'
-
1
autoload :Satisfy, 'rspec/matchers/built_in/satisfy'
-
1
autoload :StartWith, 'rspec/matchers/built_in/start_or_end_with'
-
1
autoload :ThrowSymbol, 'rspec/matchers/built_in/throw_symbol'
-
1
autoload :YieldControl, 'rspec/matchers/built_in/yield'
-
1
autoload :YieldSuccessiveArgs, 'rspec/matchers/built_in/yield'
-
1
autoload :YieldWithArgs, 'rspec/matchers/built_in/yield'
-
1
autoload :YieldWithNoArgs, 'rspec/matchers/built_in/yield'
-
end
-
end
-
end
-
1
module RSpec
-
1
module Matchers
-
1
module BuiltIn
-
# @api private
-
#
-
# Used _internally_ as a base class for matchers that ship with
-
# rspec-expectations and rspec-rails.
-
#
-
# ### Warning:
-
#
-
# This class is for internal use, and subject to change without notice. We
-
# strongly recommend that you do not base your custom matchers on this
-
# class. If/when this changes, we will announce it and remove this warning.
-
1
class BaseMatcher
-
1
include RSpec::Matchers::Composable
-
-
# @api private
-
# Used to detect when no arg is passed to `initialize`.
-
# `nil` cannot be used because it's a valid value to pass.
-
1
UNDEFINED = Object.new.freeze
-
-
# @private
-
1
attr_reader :actual, :expected, :rescued_exception
-
-
1
def initialize(expected=UNDEFINED)
-
37
@expected = expected unless UNDEFINED.equal?(expected)
-
end
-
-
# @api private
-
# Indicates if the match is successful. Delegates to `match`, which
-
# should be defined on a subclass. Takes care of consistently
-
# initializing the `actual` attribute.
-
1
def matches?(actual)
-
37
@actual = actual
-
37
match(expected, actual)
-
end
-
-
# @api private
-
# Used to wrap a block of code that will indicate failure by
-
# raising one of the named exceptions.
-
#
-
# This is used by rspec-rails for some of its matchers that
-
# wrap rails' assertions.
-
1
def match_unless_raises(*exceptions)
-
exceptions.unshift Exception if exceptions.empty?
-
begin
-
yield
-
true
-
rescue *exceptions => @rescued_exception
-
false
-
end
-
end
-
-
# @api private
-
# Generates a description using {EnglishPhrasing}.
-
# @return [String]
-
1
def description
-
desc = EnglishPhrasing.split_words(self.class.matcher_name)
-
desc << EnglishPhrasing.list(@expected) if defined?(@expected)
-
desc
-
end
-
-
# @api private
-
# Matchers are not diffable by default. Override this to make your
-
# subclass diffable.
-
1
def diffable?
-
false
-
end
-
-
# @api private
-
# Most matchers are value matchers (i.e. meant to work with `expect(value)`)
-
# rather than block matchers (i.e. meant to work with `expect { }`), so
-
# this defaults to false. Block matchers must override this to return true.
-
1
def supports_block_expectations?
-
false
-
end
-
-
# @api private
-
1
def expects_call_stack_jump?
-
false
-
end
-
-
# @private
-
1
def expected_formatted
-
RSpec::Support::ObjectFormatter.format(@expected)
-
end
-
-
# @private
-
1
def actual_formatted
-
RSpec::Support::ObjectFormatter.format(@actual)
-
end
-
-
# @private
-
1
def self.matcher_name
-
@matcher_name ||= underscore(name.split("::").last)
-
end
-
-
# @private
-
# Borrowed from ActiveSupport.
-
1
def self.underscore(camel_cased_word)
-
word = camel_cased_word.to_s.dup
-
word.gsub!(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
-
word.gsub!(/([a-z\d])([A-Z])/, '\1_\2')
-
word.tr!("-", "_")
-
word.downcase!
-
word
-
end
-
1
private_class_method :underscore
-
-
1
private
-
-
1
def assert_ivars(*expected_ivars)
-
return unless (expected_ivars - present_ivars).any?
-
ivar_list = EnglishPhrasing.list(expected_ivars)
-
raise "#{self.class.name} needs to supply#{ivar_list}"
-
end
-
-
1
if RUBY_VERSION.to_f < 1.9
-
# :nocov:
-
skipped
def present_ivars
-
skipped
instance_variables.map { |v| v.to_sym }
-
skipped
end
-
# :nocov:
-
else
-
1
alias present_ivars instance_variables
-
end
-
-
# @private
-
1
module HashFormatting
-
# `{ :a => 5, :b => 2 }.inspect` produces:
-
#
-
# {:a=>5, :b=>2}
-
#
-
# ...but it looks much better as:
-
#
-
# {:a => 5, :b => 2}
-
#
-
# This is idempotent and safe to run on a string multiple times.
-
1
def improve_hash_formatting(inspect_string)
-
inspect_string.gsub(/(\S)=>(\S)/, '\1 => \2')
-
end
-
1
module_function :improve_hash_formatting
-
end
-
-
1
include HashFormatting
-
-
# @api private
-
# Provides default implementations of failure messages, based on the `description`.
-
1
module DefaultFailureMessages
-
# @api private
-
# Provides a good generic failure message. Based on `description`.
-
# When subclassing, if you are not satisfied with this failure message
-
# you often only need to override `description`.
-
# @return [String]
-
1
def failure_message
-
"expected #{description_of @actual} to #{description}"
-
end
-
-
# @api private
-
# Provides a good generic negative failure message. Based on `description`.
-
# When subclassing, if you are not satisfied with this failure message
-
# you often only need to override `description`.
-
# @return [String]
-
1
def failure_message_when_negated
-
"expected #{description_of @actual} not to #{description}"
-
end
-
-
# @private
-
1
def self.has_default_failure_messages?(matcher)
-
matcher.method(:failure_message).owner == self &&
-
matcher.method(:failure_message_when_negated).owner == self
-
rescue NameError
-
false
-
end
-
end
-
-
1
include DefaultFailureMessages
-
end
-
end
-
end
-
end
-
1
module RSpec
-
1
module Matchers
-
1
module BuiltIn
-
# @api private
-
# Provides the implementation for `contain_exactly` and `match_array`.
-
# Not intended to be instantiated directly.
-
1
class ContainExactly < BaseMatcher
-
# @api private
-
# @return [String]
-
1
def failure_message
-
if Array === actual
-
message = "expected collection contained: #{description_of(safe_sort(surface_descriptions_in expected))}\n"
-
message += "actual collection contained: #{description_of(safe_sort(actual))}\n"
-
message += "the missing elements were: #{description_of(safe_sort(surface_descriptions_in missing_items))}\n" unless missing_items.empty?
-
message += "the extra elements were: #{description_of(safe_sort(extra_items))}\n" unless extra_items.empty?
-
message
-
else
-
"expected a collection that can be converted to an array with " \
-
"`#to_ary` or `#to_a`, but got #{actual_formatted}"
-
end
-
end
-
-
# @api private
-
# @return [String]
-
1
def failure_message_when_negated
-
list = EnglishPhrasing.list(surface_descriptions_in(expected))
-
"expected #{actual_formatted} not to contain exactly#{list}"
-
end
-
-
# @api private
-
# @return [String]
-
1
def description
-
list = EnglishPhrasing.list(surface_descriptions_in(expected))
-
"contain exactly#{list}"
-
end
-
-
1
private
-
-
1
def match(_expected, _actual)
-
36
return false unless convert_actual_to_an_array
-
36
match_when_sorted? || (extra_items.empty? && missing_items.empty?)
-
end
-
-
# This cannot always work (e.g. when dealing with unsortable items,
-
# or matchers as expected items), but it's practically free compared to
-
# the slowness of the full matching algorithm, and in common cases this
-
# works, so it's worth a try.
-
1
def match_when_sorted?
-
36
values_match?(safe_sort(expected), safe_sort(actual))
-
end
-
-
1
def convert_actual_to_an_array
-
36
if actual.respond_to?(:to_ary)
-
36
@actual = actual.to_ary
-
elsif should_enumerate?(actual) && actual.respond_to?(:to_a)
-
@actual = actual.to_a
-
else
-
return false
-
end
-
end
-
-
1
def safe_sort(array)
-
72
array.sort
-
rescue Exception
-
array
-
end
-
-
1
def missing_items
-
@missing_items ||= best_solution.unmatched_expected_indexes.map do |index|
-
expected[index]
-
end
-
end
-
-
1
def extra_items
-
@extra_items ||= best_solution.unmatched_actual_indexes.map do |index|
-
62
actual[index]
-
28
end
-
end
-
-
1
def best_solution
-
28
@best_solution ||= pairings_maximizer.find_best_solution
-
end
-
-
1
def pairings_maximizer
-
@pairings_maximizer ||= begin
-
112
expected_matches = Hash[Array.new(expected.size) { |i| [i, []] }]
-
112
actual_matches = Hash[Array.new(actual.size) { |i| [i, []] }]
-
-
28
expected.each_with_index do |e, ei|
-
84
actual.each_with_index do |a, ai|
-
252
next unless values_match?(e, a)
-
-
22
expected_matches[ei] << ai
-
22
actual_matches[ai] << ei
-
end
-
end
-
-
28
PairingsMaximizer.new(expected_matches, actual_matches)
-
28
end
-
end
-
-
# Once we started supporting composing matchers, the algorithm for this matcher got
-
# much more complicated. Consider this expression:
-
#
-
# expect(["fool", "food"]).to contain_exactly(/foo/, /fool/)
-
#
-
# This should pass (because we can pair /fool/ with "fool" and /foo/ with "food"), but
-
# the original algorithm used by this matcher would pair the first elements it could
-
# (/foo/ with "fool"), which would leave /fool/ and "food" unmatched. When we have
-
# an expected element which is a matcher that matches a superset of actual items
-
# compared to another expected element matcher, we need to consider every possible pairing.
-
#
-
# This class is designed to maximize the number of actual/expected pairings -- or,
-
# conversely, to minimize the number of unpaired items. It's essentially a brute
-
# force solution, but with a few heuristics applied to reduce the size of the
-
# problem space:
-
#
-
# * Any items which match none of the items in the other list are immediately
-
# placed into the `unmatched_expected_indexes` or `unmatched_actual_indexes` array.
-
# The extra items and missing items in the matcher failure message are derived
-
# from these arrays.
-
# * Any items which reciprocally match only each other are paired up and not
-
# considered further.
-
#
-
# What's left is only the items which match multiple items from the other list
-
# (or vice versa). From here, it performs a brute-force depth-first search,
-
# looking for a solution which pairs all elements in both lists, or, barring that,
-
# that produces the fewest unmatched items.
-
#
-
# @private
-
1
class PairingsMaximizer
-
1
Solution = Struct.new(:unmatched_expected_indexes, :unmatched_actual_indexes,
-
:indeterminate_expected_indexes, :indeterminate_actual_indexes) do
-
1
def worse_than?(other)
-
unmatched_item_count > other.unmatched_item_count
-
end
-
-
1
def candidate?
-
indeterminate_expected_indexes.empty? &&
-
28
indeterminate_actual_indexes.empty?
-
end
-
-
1
def ideal?
-
candidate? && (
-
unmatched_expected_indexes.empty? ||
-
unmatched_actual_indexes.empty?
-
)
-
end
-
-
1
def unmatched_item_count
-
unmatched_expected_indexes.count + unmatched_actual_indexes.count
-
end
-
-
1
def +(derived_candidate_solution)
-
self.class.new(
-
unmatched_expected_indexes + derived_candidate_solution.unmatched_expected_indexes,
-
unmatched_actual_indexes + derived_candidate_solution.unmatched_actual_indexes,
-
# Ignore the indeterminate indexes: by the time we get here,
-
# we've dealt with all indeterminates.
-
[], []
-
)
-
end
-
end
-
-
1
attr_reader :expected_to_actual_matched_indexes, :actual_to_expected_matched_indexes, :solution
-
-
1
def initialize(expected_to_actual_matched_indexes, actual_to_expected_matched_indexes)
-
28
@expected_to_actual_matched_indexes = expected_to_actual_matched_indexes
-
28
@actual_to_expected_matched_indexes = actual_to_expected_matched_indexes
-
-
28
unmatched_expected_indexes, indeterminate_expected_indexes =
-
categorize_indexes(expected_to_actual_matched_indexes, actual_to_expected_matched_indexes)
-
-
28
unmatched_actual_indexes, indeterminate_actual_indexes =
-
categorize_indexes(actual_to_expected_matched_indexes, expected_to_actual_matched_indexes)
-
-
28
@solution = Solution.new(unmatched_expected_indexes, unmatched_actual_indexes,
-
indeterminate_expected_indexes, indeterminate_actual_indexes)
-
end
-
-
1
def find_best_solution
-
28
return solution if solution.candidate?
-
best_solution_so_far = NullSolution
-
-
expected_index = solution.indeterminate_expected_indexes.first
-
actuals = expected_to_actual_matched_indexes[expected_index]
-
-
actuals.each do |actual_index|
-
solution = best_solution_for_pairing(expected_index, actual_index)
-
return solution if solution.ideal?
-
best_solution_so_far = solution if best_solution_so_far.worse_than?(solution)
-
end
-
-
best_solution_so_far
-
end
-
-
1
private
-
-
# @private
-
# Starting solution that is worse than any other real solution.
-
1
NullSolution = Class.new do
-
1
def self.worse_than?(_other)
-
true
-
end
-
end
-
-
1
def categorize_indexes(indexes_to_categorize, other_indexes)
-
56
unmatched = []
-
56
indeterminate = []
-
-
56
indexes_to_categorize.each_pair do |index, matches|
-
168
if matches.empty?
-
124
unmatched << index
-
elsif !reciprocal_single_match?(matches, index, other_indexes)
-
indeterminate << index
-
end
-
end
-
-
56
return unmatched, indeterminate
-
end
-
-
1
def reciprocal_single_match?(matches, index, other_list)
-
44
return false unless matches.one?
-
44
other_list[matches.first] == [index]
-
end
-
-
1
def best_solution_for_pairing(expected_index, actual_index)
-
modified_expecteds = apply_pairing_to(
-
solution.indeterminate_expected_indexes,
-
expected_to_actual_matched_indexes, actual_index)
-
-
modified_expecteds.delete(expected_index)
-
-
modified_actuals = apply_pairing_to(
-
solution.indeterminate_actual_indexes,
-
actual_to_expected_matched_indexes, expected_index)
-
-
modified_actuals.delete(actual_index)
-
-
solution + self.class.new(modified_expecteds, modified_actuals).find_best_solution
-
end
-
-
1
def apply_pairing_to(indeterminates, original_matches, other_list_index)
-
indeterminates.inject({}) do |accum, index|
-
accum[index] = original_matches[index] - [other_list_index]
-
accum
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
1
module RSpec
-
1
module Matchers
-
1
module BuiltIn
-
# @api private
-
# Provides the implementation for `eq`.
-
# Not intended to be instantiated directly.
-
1
class Eq < BaseMatcher
-
# @api private
-
# @return [String]
-
1
def failure_message
-
"\nexpected: #{expected_formatted}\n got: #{actual_formatted}\n\n(compared using ==)\n"
-
end
-
-
# @api private
-
# @return [String]
-
1
def failure_message_when_negated
-
"\nexpected: value != #{expected_formatted}\n got: #{actual_formatted}\n\n(compared using ==)\n"
-
end
-
-
# @api private
-
# @return [String]
-
1
def description
-
"eq #{expected_formatted}"
-
end
-
-
# @api private
-
# @return [Boolean]
-
1
def diffable?
-
true
-
end
-
-
1
private
-
-
1
def match(expected, actual)
-
1
actual == expected
-
end
-
end
-
end
-
end
-
end
-
1
RSpec::Support.require_rspec_support "fuzzy_matcher"
-
-
1
module RSpec
-
1
module Matchers
-
# Mixin designed to support the composable matcher features
-
# of RSpec 3+. Mix it into your custom matcher classes to
-
# allow them to be used in a composable fashion.
-
#
-
# @api public
-
1
module Composable
-
# Creates a compound `and` expectation. The matcher will
-
# only pass if both sub-matchers pass.
-
# This can be chained together to form an arbitrarily long
-
# chain of matchers.
-
#
-
# @example
-
# expect(alphabet).to start_with("a").and end_with("z")
-
# expect(alphabet).to start_with("a") & end_with("z")
-
#
-
# @note The negative form (`expect(...).not_to matcher.and other`)
-
# is not supported at this time.
-
1
def and(matcher)
-
BuiltIn::Compound::And.new self, matcher
-
end
-
1
alias & and
-
-
# Creates a compound `or` expectation. The matcher will
-
# pass if either sub-matcher passes.
-
# This can be chained together to form an arbitrarily long
-
# chain of matchers.
-
#
-
# @example
-
# expect(stoplight.color).to eq("red").or eq("green").or eq("yellow")
-
# expect(stoplight.color).to eq("red") | eq("green") | eq("yellow")
-
#
-
# @note The negative form (`expect(...).not_to matcher.or other`)
-
# is not supported at this time.
-
1
def or(matcher)
-
BuiltIn::Compound::Or.new self, matcher
-
end
-
1
alias | or
-
-
# Delegates to `#matches?`. Allows matchers to be used in composable
-
# fashion and also supports using matchers in case statements.
-
1
def ===(value)
-
matches?(value)
-
end
-
-
1
private
-
-
# This provides a generic way to fuzzy-match an expected value against
-
# an actual value. It understands nested data structures (e.g. hashes
-
# and arrays) and is able to match against a matcher being used as
-
# the expected value or within the expected value at any level of
-
# nesting.
-
#
-
# Within a custom matcher you are encouraged to use this whenever your
-
# matcher needs to match two values, unless it needs more precise semantics.
-
# For example, the `eq` matcher _does not_ use this as it is meant to
-
# use `==` (and only `==`) for matching.
-
#
-
# @param expected [Object] what is expected
-
# @param actual [Object] the actual value
-
#
-
# @!visibility public
-
1
def values_match?(expected, actual)
-
288
expected = with_matchers_cloned(expected)
-
288
Support::FuzzyMatcher.values_match?(expected, actual)
-
end
-
-
# Returns the description of the given object in a way that is
-
# aware of composed matchers. If the object is a matcher with
-
# a `description` method, returns the description; otherwise
-
# returns `object.inspect`.
-
#
-
# You are encouraged to use this in your custom matcher's
-
# `description`, `failure_message` or
-
# `failure_message_when_negated` implementation if you are
-
# supporting matcher arguments.
-
#
-
# @!visibility public
-
1
def description_of(object)
-
RSpec::Support::ObjectFormatter.format(object)
-
end
-
-
# Transforms the given data structue (typically a hash or array)
-
# into a new data structure that, when `#inspect` is called on it,
-
# will provide descriptions of any contained matchers rather than
-
# the normal `#inspect` output.
-
#
-
# You are encouraged to use this in your custom matcher's
-
# `description`, `failure_message` or
-
# `failure_message_when_negated` implementation if you are
-
# supporting any arguments which may be a data structure
-
# containing matchers.
-
#
-
# @!visibility public
-
1
def surface_descriptions_in(item)
-
if Matchers.is_a_describable_matcher?(item)
-
DescribableItem.new(item)
-
elsif Hash === item
-
Hash[surface_descriptions_in(item.to_a)]
-
elsif Struct === item
-
RSpec::Support::ObjectFormatter.format(item)
-
elsif should_enumerate?(item)
-
begin
-
item.map { |subitem| surface_descriptions_in(subitem) }
-
rescue IOError # STDOUT is enumerable but `map` raises an error
-
RSpec::Support::ObjectFormatter.format(item)
-
end
-
else
-
item
-
end
-
end
-
-
# @private
-
# Historically, a single matcher instance was only checked
-
# against a single value. Given that the matcher was only
-
# used once, it's been common to memoize some intermediate
-
# calculation that is derived from the `actual` value in
-
# order to reuse that intermediate result in the failure
-
# message.
-
#
-
# This can cause a problem when using such a matcher as an
-
# argument to another matcher in a composed matcher expression,
-
# since the matcher instance may be checked against multiple
-
# values and produce invalid results due to the memoization.
-
#
-
# To deal with this, we clone any matchers in `expected` via
-
# this method when using `values_match?`, so that any memoization
-
# does not "leak" between checks.
-
1
def with_matchers_cloned(object)
-
396
if Matchers.is_a_matcher?(object)
-
object.clone
-
396
elsif Hash === object
-
Hash[with_matchers_cloned(object.to_a)]
-
396
elsif Struct === object
-
object
-
396
elsif should_enumerate?(object)
-
36
begin
-
144
object.map { |subobject| with_matchers_cloned(subobject) }
-
rescue IOError # STDOUT is enumerable but `map` raises an error
-
object
-
end
-
else
-
360
object
-
end
-
end
-
-
1
if String.ancestors.include?(Enumerable) # 1.8.7
-
# :nocov:
-
skipped
# Strings are not enumerable on 1.9, and on 1.8 they are an infinitely
-
skipped
# nested enumerable: since ruby lacks a character class, it yields
-
skipped
# 1-character strings, which are themselves enumerable, composed of a
-
skipped
# a single 1-character string, which is an enumerable, etc.
-
skipped
#
-
skipped
# @api private
-
skipped
def should_enumerate?(item)
-
skipped
return false if String === item
-
skipped
Enumerable === item && !(Range === item)
-
skipped
end
-
# :nocov:
-
else
-
# @api private
-
1
def should_enumerate?(item)
-
396
Enumerable === item && !(Range === item)
-
end
-
end
-
1
module_function :surface_descriptions_in, :should_enumerate?
-
-
# Wraps an item in order to surface its `description` via `inspect`.
-
# @api private
-
1
DescribableItem = Struct.new(:item) do
-
1
def inspect
-
"(#{item.description})"
-
end
-
-
1
def pretty_print(pp)
-
pp.text "(#{item.description})"
-
end
-
end
-
end
-
end
-
end
-
1
module RSpec
-
1
module Matchers
-
# Defines the custom matcher DSL.
-
1
module DSL
-
# Defines a custom matcher.
-
# @see RSpec::Matchers
-
1
def define(name, &declarations)
-
1
warn_about_block_args(name, declarations)
-
1
define_method name do |*expected, &block_arg|
-
8
RSpec::Matchers::DSL::Matcher.new(name, declarations, self, *expected, &block_arg)
-
end
-
end
-
1
alias_method :matcher, :define
-
-
1
private
-
-
1
if Proc.method_defined?(:parameters)
-
1
def warn_about_block_args(name, declarations)
-
1
declarations.parameters.each do |type, arg_name|
-
1
next unless type == :block
-
RSpec.warning("Your `#{name}` custom matcher receives a block argument (`#{arg_name}`), " \
-
"but due to limitations in ruby, RSpec cannot provide the block. Instead, " \
-
"use the `block_arg` method to access the block")
-
end
-
end
-
else
-
# :nocov:
-
skipped
def warn_about_block_args(*)
-
skipped
# There's no way to detect block params on 1.8 since the method reflection APIs don't expose it
-
skipped
end
-
# :nocov:
-
end
-
-
2
RSpec.configure { |c| c.extend self } if RSpec.respond_to?(:configure)
-
-
# Contains the methods that are available from within the
-
# `RSpec::Matchers.define` DSL for creating custom matchers.
-
1
module Macros
-
# Stores the block that is used to determine whether this matcher passes
-
# or fails. The block should return a boolean value. When the matcher is
-
# passed to `expect(...).to` and the block returns `true`, then the expectation
-
# passes. Similarly, when the matcher is passed to `expect(...).not_to` and the
-
# block returns `false`, then the expectation passes.
-
#
-
# @example
-
#
-
# RSpec::Matchers.define :be_even do
-
# match do |actual|
-
# actual.even?
-
# end
-
# end
-
#
-
# expect(4).to be_even # passes
-
# expect(3).not_to be_even # passes
-
# expect(3).to be_even # fails
-
# expect(4).not_to be_even # fails
-
#
-
# @yield [Object] actual the actual value (i.e. the value wrapped by `expect`)
-
1
def match(&match_block)
-
8
define_user_override(:matches?, match_block) do |actual|
-
8
begin
-
8
@actual = actual
-
8
RSpec::Support.with_failure_notifier(RAISE_NOTIFIER) do
-
8
super(*actual_arg_for(match_block))
-
end
-
rescue RSpec::Expectations::ExpectationNotMetError
-
false
-
end
-
end
-
end
-
-
# @private
-
1
RAISE_NOTIFIER = Proc.new { |err, _opts| raise err }
-
-
# Use this to define the block for a negative expectation (`expect(...).not_to`)
-
# when the positive and negative forms require different handling. This
-
# is rarely necessary, but can be helpful, for example, when specifying
-
# asynchronous processes that require different timeouts.
-
#
-
# @yield [Object] actual the actual value (i.e. the value wrapped by `expect`)
-
1
def match_when_negated(&match_block)
-
define_user_override(:does_not_match?, match_block) do |actual|
-
@actual = actual
-
super(*actual_arg_for(match_block))
-
end
-
end
-
-
# Use this instead of `match` when the block will raise an exception
-
# rather than returning false to indicate a failure.
-
#
-
# @example
-
#
-
# RSpec::Matchers.define :accept_as_valid do |candidate_address|
-
# match_unless_raises ValidationException do |validator|
-
# validator.validate(candidate_address)
-
# end
-
# end
-
#
-
# expect(email_validator).to accept_as_valid("person@company.com")
-
#
-
# @yield [Object] actual the actual object (i.e. the value wrapped by `expect`)
-
1
def match_unless_raises(expected_exception=Exception, &match_block)
-
define_user_override(:matches?, match_block) do |actual|
-
@actual = actual
-
begin
-
super(*actual_arg_for(match_block))
-
rescue expected_exception => @rescued_exception
-
false
-
else
-
true
-
end
-
end
-
end
-
-
# Customizes the failure messsage to use when this matcher is
-
# asked to positively match. Only use this when the message
-
# generated by default doesn't suit your needs.
-
#
-
# @example
-
#
-
# RSpec::Matchers.define :have_strength do |expected|
-
# match { your_match_logic }
-
#
-
# failure_message do |actual|
-
# "Expected strength of #{expected}, but had #{actual.strength}"
-
# end
-
# end
-
#
-
# @yield [Object] actual the actual object (i.e. the value wrapped by `expect`)
-
1
def failure_message(&definition)
-
define_user_override(__method__, definition)
-
end
-
-
# Customize the failure messsage to use when this matcher is asked
-
# to negatively match. Only use this when the message generated by
-
# default doesn't suit your needs.
-
#
-
# @example
-
#
-
# RSpec::Matchers.define :have_strength do |expected|
-
# match { your_match_logic }
-
#
-
# failure_message_when_negated do |actual|
-
# "Expected not to have strength of #{expected}, but did"
-
# end
-
# end
-
#
-
# @yield [Object] actual the actual object (i.e. the value wrapped by `expect`)
-
1
def failure_message_when_negated(&definition)
-
define_user_override(__method__, definition)
-
end
-
-
# Customize the description to use for one-liners. Only use this when
-
# the description generated by default doesn't suit your needs.
-
#
-
# @example
-
#
-
# RSpec::Matchers.define :qualify_for do |expected|
-
# match { your_match_logic }
-
#
-
# description do
-
# "qualify for #{expected}"
-
# end
-
# end
-
#
-
# @yield [Object] actual the actual object (i.e. the value wrapped by `expect`)
-
1
def description(&definition)
-
define_user_override(__method__, definition)
-
end
-
-
# Tells the matcher to diff the actual and expected values in the failure
-
# message.
-
1
def diffable
-
define_method(:diffable?) { true }
-
end
-
-
# Declares that the matcher can be used in a block expectation.
-
# Users will not be able to use your matcher in a block
-
# expectation without declaring this.
-
# (e.g. `expect { do_something }.to matcher`).
-
1
def supports_block_expectations
-
define_method(:supports_block_expectations?) { true }
-
end
-
-
# Convenience for defining methods on this matcher to create a fluent
-
# interface. The trick about fluent interfaces is that each method must
-
# return self in order to chain methods together. `chain` handles that
-
# for you. If the method is invoked and the
-
# `include_chain_clauses_in_custom_matcher_descriptions` config option
-
# hash been enabled, the chained method name and args will be added to the
-
# default description and failure message.
-
#
-
# In the common case where you just want the chained method to store some
-
# value(s) for later use (e.g. in `match`), you can provide one or more
-
# attribute names instead of a block; the chained method will store its
-
# arguments in instance variables with those names, and the values will
-
# be exposed via getters.
-
#
-
# @example
-
#
-
# RSpec::Matchers.define :have_errors_on do |key|
-
# chain :with do |message|
-
# @message = message
-
# end
-
#
-
# match do |actual|
-
# actual.errors[key] == @message
-
# end
-
# end
-
#
-
# expect(minor).to have_errors_on(:age).with("Not old enough to participate")
-
1
def chain(method_name, *attr_names, &definition)
-
unless block_given? ^ attr_names.any?
-
raise ArgumentError, "You must pass either a block or some attribute names (but not both) to `chain`."
-
end
-
-
definition = assign_attributes(attr_names) if attr_names.any?
-
-
define_user_override(method_name, definition) do |*args, &block|
-
super(*args, &block)
-
@chained_method_clauses.push([method_name, args])
-
self
-
end
-
end
-
-
1
def assign_attributes(attr_names)
-
attr_reader(*attr_names)
-
private(*attr_names)
-
-
lambda do |*attr_values|
-
attr_names.zip(attr_values) do |attr_name, attr_value|
-
instance_variable_set(:"@#{attr_name}", attr_value)
-
end
-
end
-
end
-
-
# assign_attributes isn't defined in the private section below because
-
# that makes MRI 1.9.2 emit a warning about private attributes.
-
1
private :assign_attributes
-
-
1
private
-
-
# Does the following:
-
#
-
# - Defines the named method using a user-provided block
-
# in @user_method_defs, which is included as an ancestor
-
# in the singleton class in which we eval the `define` block.
-
# - Defines an overriden definition for the same method
-
# usign the provided `our_def` block.
-
# - Provides a default `our_def` block for the common case
-
# of needing to call the user's definition with `@actual`
-
# as an arg, but only if their block's arity can handle it.
-
#
-
# This compiles the user block into an actual method, allowing
-
# them to use normal method constructs like `return`
-
# (e.g. for a early guard statement), while allowing us to define
-
# an override that can provide the wrapped handling
-
# (e.g. assigning `@actual`, rescueing errors, etc) and
-
# can `super` to the user's definition.
-
1
def define_user_override(method_name, user_def, &our_def)
-
8
@user_method_defs.__send__(:define_method, method_name, &user_def)
-
8
our_def ||= lambda { super(*actual_arg_for(user_def)) }
-
8
define_method(method_name, &our_def)
-
end
-
-
# Defines deprecated macro methods from RSpec 2 for backwards compatibility.
-
# @deprecated Use the methods from {Macros} instead.
-
1
module Deprecated
-
# @deprecated Use {Macros#match} instead.
-
1
def match_for_should(&definition)
-
RSpec.deprecate("`match_for_should`", :replacement => "`match`")
-
match(&definition)
-
end
-
-
# @deprecated Use {Macros#match_when_negated} instead.
-
1
def match_for_should_not(&definition)
-
RSpec.deprecate("`match_for_should_not`", :replacement => "`match_when_negated`")
-
match_when_negated(&definition)
-
end
-
-
# @deprecated Use {Macros#failure_message} instead.
-
1
def failure_message_for_should(&definition)
-
RSpec.deprecate("`failure_message_for_should`", :replacement => "`failure_message`")
-
failure_message(&definition)
-
end
-
-
# @deprecated Use {Macros#failure_message_when_negated} instead.
-
1
def failure_message_for_should_not(&definition)
-
RSpec.deprecate("`failure_message_for_should_not`", :replacement => "`failure_message_when_negated`")
-
failure_message_when_negated(&definition)
-
end
-
end
-
end
-
-
# Defines default implementations of the matcher
-
# protocol methods for custom matchers. You can
-
# override any of these using the {RSpec::Matchers::DSL::Macros Macros} methods
-
# from within an `RSpec::Matchers.define` block.
-
1
module DefaultImplementations
-
1
include BuiltIn::BaseMatcher::DefaultFailureMessages
-
-
# @api private
-
# Used internally by objects returns by `should` and `should_not`.
-
1
def diffable?
-
false
-
end
-
-
# The default description.
-
1
def description
-
english_name = EnglishPhrasing.split_words(name)
-
expected_list = EnglishPhrasing.list(expected)
-
"#{english_name}#{expected_list}#{chained_method_clause_sentences}"
-
end
-
-
# Matchers do not support block expectations by default. You
-
# must opt-in.
-
1
def supports_block_expectations?
-
false
-
end
-
-
# Most matchers do not expect call stack jumps.
-
1
def expects_call_stack_jump?
-
false
-
end
-
-
1
private
-
-
1
def chained_method_clause_sentences
-
return '' unless Expectations.configuration.include_chain_clauses_in_custom_matcher_descriptions?
-
-
@chained_method_clauses.map do |(method_name, method_args)|
-
english_name = EnglishPhrasing.split_words(method_name)
-
arg_list = EnglishPhrasing.list(method_args)
-
" #{english_name}#{arg_list}"
-
end.join
-
end
-
end
-
-
# The class used for custom matchers. The block passed to
-
# `RSpec::Matchers.define` will be evaluated in the context
-
# of the singleton class of an instance, and will have the
-
# {RSpec::Matchers::DSL::Macros Macros} methods available.
-
1
class Matcher
-
# Provides default implementations for the matcher protocol methods.
-
1
include DefaultImplementations
-
-
# Allows expectation expressions to be used in the match block.
-
1
include RSpec::Matchers
-
-
# Supports the matcher composability features of RSpec 3+.
-
1
include Composable
-
-
# Makes the macro methods available to an `RSpec::Matchers.define` block.
-
1
extend Macros
-
1
extend Macros::Deprecated
-
-
# Exposes the value being matched against -- generally the object
-
# object wrapped by `expect`.
-
1
attr_reader :actual
-
-
# Exposes the exception raised during the matching by `match_unless_raises`.
-
# Could be useful to extract details for a failure message.
-
1
attr_reader :rescued_exception
-
-
# The block parameter used in the expectation
-
1
attr_reader :block_arg
-
-
# The name of the matcher.
-
1
attr_reader :name
-
-
# @api private
-
1
def initialize(name, declarations, matcher_execution_context, *expected, &block_arg)
-
8
@name = name
-
8
@actual = nil
-
8
@expected_as_array = expected
-
8
@matcher_execution_context = matcher_execution_context
-
8
@chained_method_clauses = []
-
8
@block_arg = block_arg
-
-
class << self
-
# See `Macros#define_user_override` above, for an explanation.
-
8
include(@user_method_defs = Module.new)
-
8
self
-
8
end.class_exec(*expected, &declarations)
-
end
-
-
# Provides the expected value. This will return an array if
-
# multiple arguments were passed to the matcher; otherwise it
-
# will return a single value.
-
# @see #expected_as_array
-
1
def expected
-
if expected_as_array.size == 1
-
expected_as_array[0]
-
else
-
expected_as_array
-
end
-
end
-
-
# Returns the expected value as an an array. This exists primarily
-
# to aid in upgrading from RSpec 2.x, since in RSpec 2, `expected`
-
# always returned an array.
-
# @see #expected
-
1
attr_reader :expected_as_array
-
-
# Adds the name (rather than a cryptic hex number)
-
# so we can identify an instance of
-
# the matcher in error messages (e.g. for `NoMethodError`)
-
1
def inspect
-
"#<#{self.class.name} #{name}>"
-
end
-
-
1
if RUBY_VERSION.to_f >= 1.9
-
# Indicates that this matcher responds to messages
-
# from the `@matcher_execution_context` as well.
-
# Also, supports getting a method object for such methods.
-
1
def respond_to_missing?(method, include_private=false)
-
super || @matcher_execution_context.respond_to?(method, include_private)
-
end
-
else # for 1.8.7
-
# :nocov:
-
skipped
# Indicates that this matcher responds to messages
-
skipped
# from the `@matcher_execution_context` as well.
-
skipped
def respond_to?(method, include_private=false)
-
skipped
super || @matcher_execution_context.respond_to?(method, include_private)
-
skipped
end
-
# :nocov:
-
end
-
-
1
private
-
-
1
def actual_arg_for(block)
-
8
block.arity.zero? ? [] : [@actual]
-
end
-
-
# Takes care of forwarding unhandled messages to the
-
# `@matcher_execution_context` (typically the current
-
# running `RSpec::Core::Example`). This is needed by
-
# rspec-rails so that it can define matchers that wrap
-
# Rails' test helper methods, but it's also a useful
-
# feature in its own right.
-
1
def method_missing(method, *args, &block)
-
if @matcher_execution_context.respond_to?(method)
-
@matcher_execution_context.__send__ method, *args, &block
-
else
-
super(method, *args, &block)
-
end
-
end
-
end
-
end
-
end
-
end
-
-
1
RSpec::Matchers.extend RSpec::Matchers::DSL
-
1
module RSpec
-
1
module Matchers
-
# Facilitates converting ruby objects to English phrases.
-
1
module EnglishPhrasing
-
# Converts a symbol into an English expression.
-
#
-
# split_words(:banana_creme_pie) #=> "banana creme pie"
-
#
-
1
def self.split_words(sym)
-
sym.to_s.gsub(/_/, ' ')
-
end
-
-
# @note The returned string has a leading space except
-
# when given an empty list.
-
#
-
# Converts an object (often a collection of objects)
-
# into an English list.
-
#
-
# list(['banana', 'kiwi', 'mango'])
-
# #=> " \"banana\", \"kiwi\", and \"mango\""
-
#
-
# Given an empty collection, returns the empty string.
-
#
-
# list([]) #=> ""
-
#
-
1
def self.list(obj)
-
return " #{RSpec::Support::ObjectFormatter.format(obj)}" if !obj || Struct === obj
-
items = Array(obj).map { |w| RSpec::Support::ObjectFormatter.format(w) }
-
case items.length
-
when 0
-
""
-
when 1
-
" #{items[0]}"
-
when 2
-
" #{items[0]} and #{items[1]}"
-
else
-
" #{items[0...-1].join(', ')}, and #{items[-1]}"
-
end
-
end
-
end
-
end
-
end
-
1
module RSpec
-
1
module Matchers
-
# @api private
-
# Handles list of expected values when there is a need to render
-
# multiple diffs. Also can handle one value.
-
1
class ExpectedsForMultipleDiffs
-
# @private
-
# Default diff label when there is only one matcher in diff
-
# output
-
1
DEFAULT_DIFF_LABEL = "Diff:".freeze
-
-
# @private
-
# Maximum readable matcher description length
-
1
DESCRIPTION_MAX_LENGTH = 65
-
-
1
def initialize(expected_list)
-
@expected_list = expected_list
-
end
-
-
# @api private
-
# Wraps provided expected value in instance of
-
# ExpectedForMultipleDiffs. If provided value is already an
-
# ExpectedForMultipleDiffs then it just returns it.
-
# @param [Any] expected value to be wrapped
-
# @return [RSpec::Matchers::ExpectedsForMultipleDiffs]
-
1
def self.from(expected)
-
return expected if self === expected
-
new([[expected, DEFAULT_DIFF_LABEL]])
-
end
-
-
# @api private
-
# Wraps provided matcher list in instance of
-
# ExpectedForMultipleDiffs.
-
# @param [Array<Any>] matchers list of matchers to wrap
-
# @return [RSpec::Matchers::ExpectedsForMultipleDiffs]
-
1
def self.for_many_matchers(matchers)
-
new(matchers.map { |m| [m.expected, diff_label_for(m)] })
-
end
-
-
# @api private
-
# Returns message with diff(s) appended for provided differ
-
# factory and actual value if there are any
-
# @param [String] message original failure message
-
# @param [Proc] differ
-
# @param [Any] actual value
-
# @return [String]
-
1
def message_with_diff(message, differ, actual)
-
diff = diffs(differ, actual)
-
message = "#{message}\n#{diff}" unless diff.empty?
-
message
-
end
-
-
1
private
-
-
1
def self.diff_label_for(matcher)
-
"Diff for (#{truncated(RSpec::Support::ObjectFormatter.format(matcher))}):"
-
end
-
-
1
def self.truncated(description)
-
return description if description.length <= DESCRIPTION_MAX_LENGTH
-
description[0...DESCRIPTION_MAX_LENGTH - 3] << "..."
-
end
-
-
1
def diffs(differ, actual)
-
@expected_list.map do |(expected, diff_label)|
-
diff = differ.diff(actual, expected)
-
next if diff.strip.empty?
-
"#{diff_label}#{diff}"
-
end.compact.join("\n")
-
end
-
end
-
end
-
end
-
1
module RSpec
-
1
module Matchers
-
1
class << self
-
# @private
-
1
attr_accessor :last_matcher, :last_expectation_handler
-
end
-
-
# @api private
-
# Used by rspec-core to clear the state used to generate
-
# descriptions after an example.
-
1
def self.clear_generated_description
-
2
self.last_matcher = nil
-
2
self.last_expectation_handler = nil
-
end
-
-
# @api private
-
# Generates an an example description based on the last expectation.
-
# Used by rspec-core's one-liner syntax.
-
1
def self.generated_description
-
return nil if last_expectation_handler.nil?
-
"#{last_expectation_handler.verb} #{last_description}"
-
end
-
-
1
private
-
-
1
def self.last_description
-
last_matcher.respond_to?(:description) ? last_matcher.description : <<-MESSAGE
-
When you call a matcher in an example without a String, like this:
-
-
specify { expect(object).to matcher }
-
-
or this:
-
-
it { is_expected.to matcher }
-
-
RSpec expects the matcher to have a #description method. You should either
-
add a String to the example this matcher is being used in, or give it a
-
description method. Then you won't have to suffer this lengthy warning again.
-
MESSAGE
-
end
-
end
-
end
-
1
module RSpec
-
1
module Matchers
-
# Provides the necessary plumbing to wrap a matcher with a decorator.
-
# @private
-
1
class MatcherDelegator
-
1
include Composable
-
1
attr_reader :base_matcher
-
-
1
def initialize(base_matcher)
-
@base_matcher = base_matcher
-
end
-
-
1
def method_missing(*args, &block)
-
base_matcher.__send__(*args, &block)
-
end
-
-
1
if ::RUBY_VERSION.to_f > 1.8
-
1
def respond_to_missing?(name, include_all=false)
-
super || base_matcher.respond_to?(name, include_all)
-
end
-
else
-
# :nocov:
-
skipped
def respond_to?(name, include_all=false)
-
skipped
super || base_matcher.respond_to?(name, include_all)
-
skipped
end
-
# :nocov:
-
end
-
-
1
def initialize_copy(other)
-
@base_matcher = @base_matcher.clone
-
super
-
end
-
end
-
end
-
end
-
1
require 'rspec/support'
-
1
RSpec::Support.require_rspec_support 'caller_filter'
-
1
RSpec::Support.require_rspec_support 'warnings'
-
1
RSpec::Support.require_rspec_support 'ruby_features'
-
-
23
RSpec::Support.define_optimized_require_for_rspec(:mocks) { |f| require_relative f }
-
-
%w[
-
instance_method_stasher
-
method_double
-
argument_matchers
-
example_methods
-
proxy
-
test_double
-
argument_list_matcher
-
message_expectation
-
order_group
-
error_generator
-
space
-
mutate_const
-
targets
-
syntax
-
configuration
-
verifying_double
-
version
-
18
].each { |name| RSpec::Support.require_rspec_mocks name }
-
-
# Share the top-level RSpec namespace, because we are a core supported
-
# extension.
-
1
module RSpec
-
# Contains top-level utility methods. While this contains a few
-
# public methods, these are not generally meant to be called from
-
# a test or example. They exist primarily for integration with
-
# test frameworks (such as rspec-core).
-
1
module Mocks
-
# Performs per-test/example setup. This should be called before
-
# an test or example begins.
-
1
def self.setup
-
2
@space_stack << (@space = space.new_scope)
-
end
-
-
# Verifies any message expectations that were set during the
-
# test or example. This should be called at the end of an example.
-
1
def self.verify
-
1
space.verify_all
-
end
-
-
# Cleans up all test double state (including any methods that were
-
# redefined on partial doubles). This _must_ be called after
-
# each example, even if an error was raised during the example.
-
1
def self.teardown
-
2
space.reset_all
-
2
@space_stack.pop
-
2
@space = @space_stack.last || @root_space
-
end
-
-
# Adds an allowance (stub) on `subject`
-
#
-
# @param subject the subject to which the message will be added
-
# @param message a symbol, representing the message that will be
-
# added.
-
# @param opts a hash of options, :expected_from is used to set the
-
# original call site
-
# @yield an optional implementation for the allowance
-
#
-
# @example Defines the implementation of `foo` on `bar`, using the passed block
-
# x = 0
-
# RSpec::Mocks.allow_message(bar, :foo) { x += 1 }
-
1
def self.allow_message(subject, message, opts={}, &block)
-
space.proxy_for(subject).add_stub(message, opts, &block)
-
end
-
-
# Sets a message expectation on `subject`.
-
# @param subject the subject on which the message will be expected
-
# @param message a symbol, representing the message that will be
-
# expected.
-
# @param opts a hash of options, :expected_from is used to set the
-
# original call site
-
# @yield an optional implementation for the expectation
-
#
-
# @example Expect the message `foo` to receive `bar`, then call it
-
# RSpec::Mocks.expect_message(bar, :foo)
-
# bar.foo
-
1
def self.expect_message(subject, message, opts={}, &block)
-
space.proxy_for(subject).add_message_expectation(message, opts, &block)
-
end
-
-
# Call the passed block and verify mocks after it has executed. This allows
-
# mock usage in arbitrary places, such as a `before(:all)` hook.
-
1
def self.with_temporary_scope
-
setup
-
-
begin
-
yield
-
verify
-
ensure
-
teardown
-
end
-
end
-
-
1
class << self
-
# @private
-
1
attr_reader :space
-
end
-
1
@space_stack = []
-
1
@root_space = @space = RSpec::Mocks::RootSpace.new
-
-
# @private
-
1
IGNORED_BACKTRACE_LINE = 'this backtrace line is ignored'
-
-
# To speed up boot time a bit, delay loading optional or rarely
-
# used features until their first use.
-
1
autoload :AnyInstance, "rspec/mocks/any_instance"
-
1
autoload :ExpectChain, "rspec/mocks/message_chain"
-
1
autoload :StubChain, "rspec/mocks/message_chain"
-
1
autoload :MarshalExtension, "rspec/mocks/marshal_extension"
-
-
# Namespace for mock-related matchers.
-
1
module Matchers
-
1
autoload :HaveReceived, "rspec/mocks/matchers/have_received"
-
1
autoload :Receive, "rspec/mocks/matchers/receive"
-
1
autoload :ReceiveMessageChain, "rspec/mocks/matchers/receive_message_chain"
-
1
autoload :ReceiveMessages, "rspec/mocks/matchers/receive_messages"
-
end
-
end
-
end
-
# We intentionally do not use the `RSpec::Support.require...` methods
-
# here so that this file can be loaded individually, as documented
-
# below.
-
1
require 'rspec/mocks/argument_matchers'
-
1
require 'rspec/support/fuzzy_matcher'
-
-
1
module RSpec
-
1
module Mocks
-
# Wrapper for matching arguments against a list of expected values. Used by
-
# the `with` method on a `MessageExpectation`:
-
#
-
# expect(object).to receive(:message).with(:a, 'b', 3)
-
# object.message(:a, 'b', 3)
-
#
-
# Values passed to `with` can be literal values or argument matchers that
-
# match against the real objects .e.g.
-
#
-
# expect(object).to receive(:message).with(hash_including(:a => 'b'))
-
#
-
# Can also be used directly to match the contents of any `Array`. This
-
# enables 3rd party mocking libs to take advantage of rspec's argument
-
# matching without using the rest of rspec-mocks.
-
#
-
# require 'rspec/mocks/argument_list_matcher'
-
# include RSpec::Mocks::ArgumentMatchers
-
#
-
# arg_list_matcher = RSpec::Mocks::ArgumentListMatcher.new(123, hash_including(:a => 'b'))
-
# arg_list_matcher.args_match?(123, :a => 'b')
-
#
-
# This class is immutable.
-
#
-
# @see ArgumentMatchers
-
1
class ArgumentListMatcher
-
# @private
-
1
attr_reader :expected_args
-
-
# @api public
-
# @param [Array] expected_args a list of expected literals and/or argument matchers
-
#
-
# Initializes an `ArgumentListMatcher` with a collection of literal
-
# values and/or argument matchers.
-
#
-
# @see ArgumentMatchers
-
# @see #args_match?
-
1
def initialize(*expected_args)
-
1
@expected_args = expected_args
-
1
ensure_expected_args_valid!
-
end
-
-
# @api public
-
# @param [Array] args
-
#
-
# Matches each element in the `expected_args` against the element in the same
-
# position of the arguments passed to `new`.
-
#
-
# @see #initialize
-
1
def args_match?(*args)
-
Support::FuzzyMatcher.values_match?(resolve_expected_args_based_on(args), args)
-
end
-
-
# @private
-
# Resolves abstract arg placeholders like `no_args` and `any_args` into
-
# a more concrete arg list based on the provided `actual_args`.
-
1
def resolve_expected_args_based_on(actual_args)
-
return [] if [ArgumentMatchers::NoArgsMatcher::INSTANCE] == expected_args
-
-
any_args_index = expected_args.index { |a| ArgumentMatchers::AnyArgsMatcher::INSTANCE == a }
-
return expected_args unless any_args_index
-
-
replace_any_args_with_splat_of_anything(any_args_index, actual_args.count)
-
end
-
-
1
private
-
-
1
def replace_any_args_with_splat_of_anything(before_count, actual_args_count)
-
any_args_count = actual_args_count - expected_args.count + 1
-
after_count = expected_args.count - before_count - 1
-
-
any_args = 1.upto(any_args_count).map { ArgumentMatchers::AnyArgMatcher::INSTANCE }
-
expected_args.first(before_count) + any_args + expected_args.last(after_count)
-
end
-
-
1
def ensure_expected_args_valid!
-
2
if expected_args.count { |a| ArgumentMatchers::AnyArgsMatcher::INSTANCE == a } > 1
-
raise ArgumentError, "`any_args` can only be passed to " \
-
"`with` once but you have passed it multiple times."
-
1
elsif expected_args.count > 1 && expected_args.any? { |a| ArgumentMatchers::NoArgsMatcher::INSTANCE == a }
-
raise ArgumentError, "`no_args` can only be passed as a " \
-
"singleton argument to `with` (i.e. `with(no_args)`), " \
-
"but you have passed additional arguments."
-
end
-
end
-
-
# Value that will match all argument lists.
-
#
-
# @private
-
1
MATCH_ALL = new(ArgumentMatchers::AnyArgsMatcher::INSTANCE)
-
end
-
end
-
end
-
# This cannot take advantage of our relative requires, since this file is a
-
# dependency of `rspec/mocks/argument_list_matcher.rb`. See comment there for
-
# details.
-
1
require 'rspec/support/matcher_definition'
-
-
1
module RSpec
-
1
module Mocks
-
# ArgumentMatchers are placeholders that you can include in message
-
# expectations to match arguments against a broader check than simple
-
# equality.
-
#
-
# With the exception of `any_args` and `no_args`, they all match against
-
# the arg in same position in the argument list.
-
#
-
# @see ArgumentListMatcher
-
1
module ArgumentMatchers
-
# Acts like an arg splat, matching any number of args at any point in an arg list.
-
#
-
# @example
-
# expect(object).to receive(:message).with(1, 2, any_args)
-
#
-
# # matches any of these:
-
# object.message(1, 2)
-
# object.message(1, 2, 3)
-
# object.message(1, 2, 3, 4)
-
1
def any_args
-
AnyArgsMatcher::INSTANCE
-
end
-
-
# Matches any argument at all.
-
#
-
# @example
-
# expect(object).to receive(:message).with(anything)
-
1
def anything
-
AnyArgMatcher::INSTANCE
-
end
-
-
# Matches no arguments.
-
#
-
# @example
-
# expect(object).to receive(:message).with(no_args)
-
1
def no_args
-
NoArgsMatcher::INSTANCE
-
end
-
-
# Matches if the actual argument responds to the specified messages.
-
#
-
# @example
-
# expect(object).to receive(:message).with(duck_type(:hello))
-
# expect(object).to receive(:message).with(duck_type(:hello, :goodbye))
-
1
def duck_type(*args)
-
DuckTypeMatcher.new(*args)
-
end
-
-
# Matches a boolean value.
-
#
-
# @example
-
# expect(object).to receive(:message).with(boolean())
-
1
def boolean
-
BooleanMatcher::INSTANCE
-
end
-
-
# Matches a hash that includes the specified key(s) or key/value pairs.
-
# Ignores any additional keys.
-
#
-
# @example
-
# expect(object).to receive(:message).with(hash_including(:key => val))
-
# expect(object).to receive(:message).with(hash_including(:key))
-
# expect(object).to receive(:message).with(hash_including(:key, :key2 => val2))
-
1
def hash_including(*args)
-
HashIncludingMatcher.new(ArgumentMatchers.anythingize_lonely_keys(*args))
-
end
-
-
# Matches an array that includes the specified items at least once.
-
# Ignores duplicates and additional values
-
#
-
# @example
-
# expect(object).to receive(:message).with(array_including(1,2,3))
-
# expect(object).to receive(:message).with(array_including([1,2,3]))
-
1
def array_including(*args)
-
actually_an_array = Array === args.first && args.count == 1 ? args.first : args
-
ArrayIncludingMatcher.new(actually_an_array)
-
end
-
-
# Matches a hash that doesn't include the specified key(s) or key/value.
-
#
-
# @example
-
# expect(object).to receive(:message).with(hash_excluding(:key => val))
-
# expect(object).to receive(:message).with(hash_excluding(:key))
-
# expect(object).to receive(:message).with(hash_excluding(:key, :key2 => :val2))
-
1
def hash_excluding(*args)
-
HashExcludingMatcher.new(ArgumentMatchers.anythingize_lonely_keys(*args))
-
end
-
-
1
alias_method :hash_not_including, :hash_excluding
-
-
# Matches if `arg.instance_of?(klass)`
-
#
-
# @example
-
# expect(object).to receive(:message).with(instance_of(Thing))
-
1
def instance_of(klass)
-
InstanceOf.new(klass)
-
end
-
-
1
alias_method :an_instance_of, :instance_of
-
-
# Matches if `arg.kind_of?(klass)`
-
#
-
# @example
-
# expect(object).to receive(:message).with(kind_of(Thing))
-
1
def kind_of(klass)
-
KindOf.new(klass)
-
end
-
-
1
alias_method :a_kind_of, :kind_of
-
-
# @private
-
1
def self.anythingize_lonely_keys(*args)
-
hash = args.last.class == Hash ? args.delete_at(-1) : {}
-
args.each { | arg | hash[arg] = AnyArgMatcher::INSTANCE }
-
hash
-
end
-
-
# Intended to be subclassed by stateless, immutable argument matchers.
-
# Provides a `<klass name>::INSTANCE` constant for accessing a global
-
# singleton instance of the matcher. There is no need to construct
-
# multiple instance since there is no state. It also facilities the
-
# special case logic we need for some of these matchers, by making it
-
# easy to do comparisons like: `[klass::INSTANCE] == args` rather than
-
# `args.count == 1 && klass === args.first`.
-
#
-
# @private
-
1
class SingletonMatcher
-
1
private_class_method :new
-
-
1
def self.inherited(subklass)
-
4
subklass.const_set(:INSTANCE, subklass.send(:new))
-
end
-
end
-
-
# @private
-
1
class AnyArgsMatcher < SingletonMatcher
-
1
def description
-
"*(any args)"
-
end
-
end
-
-
# @private
-
1
class AnyArgMatcher < SingletonMatcher
-
1
def ===(_other)
-
true
-
end
-
-
1
def description
-
"anything"
-
end
-
end
-
-
# @private
-
1
class NoArgsMatcher < SingletonMatcher
-
1
def description
-
"no args"
-
end
-
end
-
-
# @private
-
1
class BooleanMatcher < SingletonMatcher
-
1
def ===(value)
-
true == value || false == value
-
end
-
-
1
def description
-
"boolean"
-
end
-
end
-
-
# @private
-
1
class BaseHashMatcher
-
1
def initialize(expected)
-
@expected = expected
-
end
-
-
1
def ===(predicate, actual)
-
@expected.__send__(predicate) do |k, v|
-
actual.key?(k) && Support::FuzzyMatcher.values_match?(v, actual[k])
-
end
-
rescue NoMethodError
-
false
-
end
-
-
1
def description(name)
-
"#{name}(#{formatted_expected_hash.inspect.sub(/^\{/, "").sub(/\}$/, "")})"
-
end
-
-
1
private
-
-
1
def formatted_expected_hash
-
Hash[
-
@expected.map do |k, v|
-
k = RSpec::Support.rspec_description_for_object(k)
-
v = RSpec::Support.rspec_description_for_object(v)
-
-
[k, v]
-
end
-
]
-
end
-
end
-
-
# @private
-
1
class HashIncludingMatcher < BaseHashMatcher
-
1
def ===(actual)
-
super(:all?, actual)
-
end
-
-
1
def description
-
super("hash_including")
-
end
-
end
-
-
# @private
-
1
class HashExcludingMatcher < BaseHashMatcher
-
1
def ===(actual)
-
super(:none?, actual)
-
end
-
-
1
def description
-
super("hash_not_including")
-
end
-
end
-
-
# @private
-
1
class ArrayIncludingMatcher
-
1
def initialize(expected)
-
@expected = expected
-
end
-
-
1
def ===(actual)
-
actual = actual.uniq
-
@expected.uniq.all? do |expected_element|
-
actual.any? do |actual_element|
-
RSpec::Support::FuzzyMatcher.values_match?(expected_element, actual_element)
-
end
-
end
-
end
-
-
1
def description
-
"array_including(#{formatted_expected_values})"
-
end
-
-
1
private
-
-
1
def formatted_expected_values
-
@expected.map do |x|
-
RSpec::Support.rspec_description_for_object(x)
-
end.join(", ")
-
end
-
end
-
-
# @private
-
1
class DuckTypeMatcher
-
1
def initialize(*methods_to_respond_to)
-
@methods_to_respond_to = methods_to_respond_to
-
end
-
-
1
def ===(value)
-
@methods_to_respond_to.all? { |message| value.respond_to?(message) }
-
end
-
-
1
def description
-
"duck_type(#{@methods_to_respond_to.map(&:inspect).join(', ')})"
-
end
-
end
-
-
# @private
-
1
class InstanceOf
-
1
def initialize(klass)
-
@klass = klass
-
end
-
-
1
def ===(actual)
-
actual.instance_of?(@klass)
-
end
-
-
1
def description
-
"an_instance_of(#{@klass.name})"
-
end
-
end
-
-
# @private
-
1
class KindOf
-
1
def initialize(klass)
-
@klass = klass
-
end
-
-
1
def ===(actual)
-
actual.kind_of?(@klass)
-
end
-
-
1
def description
-
"kind of #{@klass.name}"
-
end
-
end
-
-
1
matcher_namespace = name + '::'
-
1
::RSpec::Support.register_matcher_definition do |object|
-
# This is the best we have for now. We should tag all of our matchers
-
# with a module or something so we can test for it directly.
-
#
-
# (Note Module#parent in ActiveSupport is defined in a similar way.)
-
begin
-
object.class.name.include?(matcher_namespace)
-
rescue NoMethodError
-
# Some objects, like BasicObject, don't implemented standard
-
# reflection methods.
-
false
-
end
-
end
-
end
-
end
-
end
-
1
module RSpec
-
1
module Mocks
-
# Provides configuration options for rspec-mocks.
-
1
class Configuration
-
1
def initialize
-
1
@yield_receiver_to_any_instance_implementation_blocks = true
-
1
@verify_doubled_constant_names = false
-
1
@transfer_nested_constants = false
-
1
@verify_partial_doubles = false
-
end
-
-
1
def yield_receiver_to_any_instance_implementation_blocks?
-
@yield_receiver_to_any_instance_implementation_blocks
-
end
-
-
# Sets whether or not RSpec will yield the receiving instance of a
-
# message to blocks that are used for any_instance stub implementations.
-
# When set, the first yielded argument will be the receiving instance.
-
# Defaults to `true`.
-
#
-
# @example
-
# RSpec.configure do |rspec|
-
# rspec.mock_with :rspec do |mocks|
-
# mocks.yield_receiver_to_any_instance_implementation_blocks = false
-
# end
-
# end
-
1
attr_writer :yield_receiver_to_any_instance_implementation_blocks
-
-
# Adds `stub` and `should_receive` to the given
-
# modules or classes. This is usually only necessary
-
# if you application uses some proxy classes that
-
# "strip themselves down" to a bare minimum set of
-
# methods and remove `stub` and `should_receive` in
-
# the process.
-
#
-
# @example
-
# RSpec.configure do |rspec|
-
# rspec.mock_with :rspec do |mocks|
-
# mocks.add_stub_and_should_receive_to Delegator
-
# end
-
# end
-
#
-
1
def add_stub_and_should_receive_to(*modules)
-
modules.each do |mod|
-
Syntax.enable_should(mod)
-
end
-
end
-
-
# Provides the ability to set either `expect`,
-
# `should` or both syntaxes. RSpec uses `expect`
-
# syntax by default. This is needed if you want to
-
# explicitly enable `should` syntax and/or explicitly
-
# disable `expect` syntax.
-
#
-
# @example
-
# RSpec.configure do |rspec|
-
# rspec.mock_with :rspec do |mocks|
-
# mocks.syntax = [:expect, :should]
-
# end
-
# end
-
#
-
1
def syntax=(*values)
-
1
syntaxes = values.flatten
-
1
if syntaxes.include?(:expect)
-
1
Syntax.enable_expect
-
else
-
Syntax.disable_expect
-
end
-
-
1
if syntaxes.include?(:should)
-
1
Syntax.enable_should
-
else
-
Syntax.disable_should
-
end
-
end
-
-
# Returns an array with a list of syntaxes
-
# that are enabled.
-
#
-
# @example
-
# unless RSpec::Mocks.configuration.syntax.include?(:expect)
-
# raise "this RSpec extension gem requires the rspec-mocks `:expect` syntax"
-
# end
-
#
-
1
def syntax
-
syntaxes = []
-
syntaxes << :should if Syntax.should_enabled?
-
syntaxes << :expect if Syntax.expect_enabled?
-
syntaxes
-
end
-
-
1
def verify_doubled_constant_names?
-
!!@verify_doubled_constant_names
-
end
-
-
# When this is set to true, an error will be raised when
-
# `instance_double` or `class_double` is given the name of an undefined
-
# constant. You probably only want to set this when running your entire
-
# test suite, with all production code loaded. Setting this for an
-
# isolated unit test will prevent you from being able to isolate it!
-
1
attr_writer :verify_doubled_constant_names
-
-
# Provides a way to perform customisations when verifying doubles.
-
#
-
# @example
-
# RSpec::Mocks.configuration.before_verifying_doubles do |ref|
-
# ref.some_method!
-
# end
-
1
def before_verifying_doubles(&block)
-
verifying_double_callbacks << block
-
end
-
1
alias :when_declaring_verifying_double :before_verifying_doubles
-
-
# @api private
-
# Returns an array of blocks to call when verifying doubles
-
1
def verifying_double_callbacks
-
@verifying_double_callbacks ||= []
-
end
-
-
1
def transfer_nested_constants?
-
!!@transfer_nested_constants
-
end
-
-
# Sets the default for the `transfer_nested_constants` option when
-
# stubbing constants.
-
1
attr_writer :transfer_nested_constants
-
-
# When set to true, partial mocks will be verified the same as object
-
# doubles. Any stubs will have their arguments checked against the original
-
# method, and methods that do not exist cannot be stubbed.
-
1
def verify_partial_doubles=(val)
-
@verify_partial_doubles = !!val
-
end
-
-
1
def verify_partial_doubles?
-
@verify_partial_doubles
-
end
-
-
1
if ::RSpec.respond_to?(:configuration)
-
1
def color?
-
::RSpec.configuration.color_enabled?
-
end
-
else
-
# Indicates whether or not diffs should be colored.
-
# Delegates to rspec-core's color option if rspec-core
-
# is loaded; otherwise you can set it here.
-
attr_writer :color
-
-
# Indicates whether or not diffs should be colored.
-
# Delegates to rspec-core's color option if rspec-core
-
# is loaded; otherwise you can set it here.
-
def color?
-
@color
-
end
-
end
-
-
# Monkey-patch `Marshal.dump` to enable dumping of mocked or stubbed
-
# objects. By default this will not work since RSpec mocks works by
-
# adding singleton methods that cannot be serialized. This patch removes
-
# these singleton methods before serialization. Setting to falsey removes
-
# the patch.
-
#
-
# This method is idempotent.
-
1
def patch_marshal_to_support_partial_doubles=(val)
-
if val
-
RSpec::Mocks::MarshalExtension.patch!
-
else
-
RSpec::Mocks::MarshalExtension.unpatch!
-
end
-
end
-
-
# @api private
-
# Resets the configured syntax to the default.
-
1
def reset_syntaxes_to_default
-
1
self.syntax = [:should, :expect]
-
1
RSpec::Mocks::Syntax.warn_about_should!
-
end
-
end
-
-
# Mocks specific configuration, as distinct from `RSpec.configuration`
-
# which is core RSpec configuration.
-
1
def self.configuration
-
1
@configuration ||= Configuration.new
-
end
-
-
1
configuration.reset_syntaxes_to_default
-
end
-
end
-
1
RSpec::Support.require_rspec_support "object_formatter"
-
-
1
module RSpec
-
1
module Mocks
-
# Raised when a message expectation is not satisfied.
-
1
MockExpectationError = Class.new(Exception)
-
-
# Raised when a test double is used after it has been torn
-
# down (typically at the end of an rspec-core example).
-
1
ExpiredTestDoubleError = Class.new(MockExpectationError)
-
-
# Raised when doubles or partial doubles are used outside of the per-test lifecycle.
-
1
OutsideOfExampleError = Class.new(StandardError)
-
-
# Raised when an expectation customization method (e.g. `with`,
-
# `and_return`) is called on a message expectation which has already been
-
# invoked.
-
1
MockExpectationAlreadyInvokedError = Class.new(Exception)
-
-
# Raised for situations that RSpec cannot support due to mutations made
-
# externally on arguments that RSpec is holding onto to use for later
-
# comparisons.
-
#
-
# @deprecated We no longer raise this error but the constant remains until
-
# RSpec 4 for SemVer reasons.
-
1
CannotSupportArgMutationsError = Class.new(StandardError)
-
-
# @private
-
1
UnsupportedMatcherError = Class.new(StandardError)
-
# @private
-
1
NegationUnsupportedError = Class.new(StandardError)
-
# @private
-
1
VerifyingDoubleNotDefinedError = Class.new(StandardError)
-
-
# @private
-
1
class ErrorGenerator
-
1
attr_writer :opts
-
-
1
def initialize(target=nil)
-
@target = target
-
end
-
-
# @private
-
1
def opts
-
@opts ||= {}
-
end
-
-
# @private
-
1
def raise_unexpected_message_error(message, args)
-
__raise "#{intro} received unexpected message :#{message} with #{format_args(args)}"
-
end
-
-
# @private
-
1
def raise_unexpected_message_args_error(expectation, args_for_multiple_calls, source_id=nil)
-
__raise error_message(expectation, args_for_multiple_calls), nil, source_id
-
end
-
-
# @private
-
1
def raise_missing_default_stub_error(expectation, args_for_multiple_calls)
-
message = error_message(expectation, args_for_multiple_calls)
-
message << "\n Please stub a default value first if message might be received with other args as well. \n"
-
-
__raise message
-
end
-
-
# @private
-
1
def raise_similar_message_args_error(expectation, args_for_multiple_calls, backtrace_line=nil)
-
__raise error_message(expectation, args_for_multiple_calls), backtrace_line
-
end
-
-
1
def default_error_message(expectation, expected_args, actual_args)
-
[
-
intro,
-
"received",
-
expectation.message.inspect,
-
unexpected_arguments_message(expected_args, actual_args),
-
].join(" ")
-
end
-
-
# rubocop:disable Style/ParameterLists
-
# @private
-
1
def raise_expectation_error(message, expected_received_count, argument_list_matcher,
-
actual_received_count, expectation_count_type, args,
-
backtrace_line=nil, source_id=nil)
-
expected_part = expected_part_of_expectation_error(expected_received_count, expectation_count_type, argument_list_matcher)
-
received_part = received_part_of_expectation_error(actual_received_count, args)
-
__raise "(#{intro(:unwrapped)}).#{message}#{format_args(args)}\n #{expected_part}\n #{received_part}", backtrace_line, source_id
-
end
-
# rubocop:enable Style/ParameterLists
-
-
# @private
-
1
def raise_unimplemented_error(doubled_module, method_name, object)
-
message = case object
-
when InstanceVerifyingDouble
-
"the %s class does not implement the instance method: %s" <<
-
if ObjectMethodReference.for(doubled_module, method_name).implemented?
-
". Perhaps you meant to use `class_double` instead?"
-
else
-
""
-
end
-
when ClassVerifyingDouble
-
"the %s class does not implement the class method: %s" <<
-
if InstanceMethodReference.for(doubled_module, method_name).implemented?
-
". Perhaps you meant to use `instance_double` instead?"
-
else
-
""
-
end
-
else
-
"%s does not implement: %s"
-
end
-
-
__raise message % [doubled_module.description, method_name]
-
end
-
-
# @private
-
1
def raise_non_public_error(method_name, visibility)
-
raise NoMethodError, "%s method `%s' called on %s" % [
-
visibility, method_name, intro
-
]
-
end
-
-
# @private
-
1
def raise_invalid_arguments_error(verifier)
-
__raise verifier.error_message
-
end
-
-
# @private
-
1
def raise_expired_test_double_error
-
raise ExpiredTestDoubleError,
-
"#{intro} was originally created in one example but has leaked into " \
-
"another example and can no longer be used. rspec-mocks' doubles are " \
-
"designed to only last for one example, and you need to create a new " \
-
"one in each example you wish to use it for."
-
end
-
-
# @private
-
1
def describe_expectation(verb, message, expected_received_count, _actual_received_count, args)
-
"#{verb} #{message}#{format_args(args)} #{count_message(expected_received_count)}"
-
end
-
-
# @private
-
1
def raise_out_of_order_error(message)
-
__raise "#{intro} received :#{message} out of order"
-
end
-
-
# @private
-
1
def raise_missing_block_error(args_to_yield)
-
__raise "#{intro} asked to yield |#{arg_list(args_to_yield)}| but no block was passed"
-
end
-
-
# @private
-
1
def raise_wrong_arity_error(args_to_yield, signature)
-
__raise "#{intro} yielded |#{arg_list(args_to_yield)}| to block with #{signature.description}"
-
end
-
-
# @private
-
1
def raise_only_valid_on_a_partial_double(method)
-
__raise "#{intro} is a pure test double. `#{method}` is only " \
-
"available on a partial double."
-
end
-
-
# @private
-
1
def raise_expectation_on_unstubbed_method(method)
-
__raise "#{intro} expected to have received #{method}, but that " \
-
"object is not a spy or method has not been stubbed."
-
end
-
-
# @private
-
1
def raise_expectation_on_mocked_method(method)
-
__raise "#{intro} expected to have received #{method}, but that " \
-
"method has been mocked instead of stubbed or spied."
-
end
-
-
# @private
-
1
def raise_double_negation_error(wrapped_expression)
-
__raise "Isn't life confusing enough? You've already set a " \
-
"negative message expectation and now you are trying to " \
-
"negate it again with `never`. What does an expression like " \
-
"`#{wrapped_expression}.not_to receive(:msg).never` even mean?"
-
end
-
-
# @private
-
1
def raise_verifying_double_not_defined_error(ref)
-
notify(VerifyingDoubleNotDefinedError.new(
-
"#{ref.description.inspect} is not a defined constant. " \
-
"Perhaps you misspelt it? " \
-
"Disable check with `verify_doubled_constant_names` configuration option."
-
))
-
end
-
-
# @private
-
1
def raise_have_received_disallowed(type, reason)
-
__raise "Using #{type}(...) with the `have_received` " \
-
"matcher is not supported#{reason}."
-
end
-
-
# @private
-
1
def raise_cant_constrain_count_for_negated_have_received_error(count_constraint)
-
__raise "can't use #{count_constraint} when negative"
-
end
-
-
# @private
-
1
def raise_method_not_stubbed_error(method_name)
-
__raise "The method `#{method_name}` was not stubbed or was already unstubbed"
-
end
-
-
# @private
-
1
def raise_already_invoked_error(message, calling_customization)
-
error_message = "The message expectation for #{intro}.#{message} has already been invoked " \
-
"and cannot be modified further (e.g. using `#{calling_customization}`). All message expectation " \
-
"customizations must be applied before it is used for the first time."
-
-
notify MockExpectationAlreadyInvokedError.new(error_message)
-
end
-
-
1
private
-
-
1
def received_part_of_expectation_error(actual_received_count, args)
-
"received: #{count_message(actual_received_count)}" +
-
method_call_args_description(args) do
-
actual_received_count > 0 && args.length > 0
-
end
-
end
-
-
1
def expected_part_of_expectation_error(expected_received_count, expectation_count_type, argument_list_matcher)
-
"expected: #{count_message(expected_received_count, expectation_count_type)}" +
-
method_call_args_description(argument_list_matcher.expected_args) do
-
argument_list_matcher.expected_args.length > 0
-
end
-
end
-
-
1
def method_call_args_description(args)
-
case args.first
-
when ArgumentMatchers::AnyArgsMatcher then " with any arguments"
-
when ArgumentMatchers::NoArgsMatcher then " with no arguments"
-
else
-
if yield
-
" with arguments: #{format_args(args)}"
-
else
-
""
-
end
-
end
-
end
-
-
1
def unexpected_arguments_message(expected_args_string, actual_args_string)
-
"with unexpected arguments\n expected: #{expected_args_string}\n got: #{actual_args_string}"
-
end
-
-
1
def error_message(expectation, args_for_multiple_calls)
-
expected_args = format_args(expectation.expected_args)
-
actual_args = format_received_args(args_for_multiple_calls)
-
message = default_error_message(expectation, expected_args, actual_args)
-
-
if args_for_multiple_calls.one?
-
diff = diff_message(expectation.expected_args, args_for_multiple_calls.first)
-
message << "\nDiff:#{diff}" unless diff.strip.empty?
-
end
-
-
message
-
end
-
-
1
def diff_message(expected_args, actual_args)
-
formatted_expected_args = expected_args.map do |x|
-
RSpec::Support.rspec_description_for_object(x)
-
end
-
-
formatted_expected_args, actual_args = unpack_string_args(formatted_expected_args, actual_args)
-
-
differ.diff(actual_args, formatted_expected_args)
-
end
-
-
1
def unpack_string_args(formatted_expected_args, actual_args)
-
if [formatted_expected_args, actual_args].all? { |x| list_of_exactly_one_string?(x) }
-
[formatted_expected_args.first, actual_args.first]
-
else
-
[formatted_expected_args, actual_args]
-
end
-
end
-
-
1
def list_of_exactly_one_string?(args)
-
Array === args && args.count == 1 && String === args.first
-
end
-
-
1
def differ
-
RSpec::Support::Differ.new(:color => RSpec::Mocks.configuration.color?)
-
end
-
-
1
def intro(unwrapped=false)
-
case @target
-
when TestDouble then TestDoubleFormatter.format(@target, unwrapped)
-
when Class then
-
formatted = "#{@target.inspect} (class)"
-
return formatted if unwrapped
-
"#<#{formatted}>"
-
when NilClass then "nil"
-
else @target
-
end
-
end
-
-
1
def __raise(message, backtrace_line=nil, source_id=nil)
-
message = opts[:message] unless opts[:message].nil?
-
exception = RSpec::Mocks::MockExpectationError.new(message)
-
prepend_to_backtrace(exception, backtrace_line) if backtrace_line
-
notify exception, :source_id => source_id
-
end
-
-
1
if RSpec::Support::Ruby.jruby?
-
def prepend_to_backtrace(exception, line)
-
raise exception
-
rescue RSpec::Mocks::MockExpectationError => with_backtrace
-
with_backtrace.backtrace.unshift(line)
-
end
-
else
-
1
def prepend_to_backtrace(exception, line)
-
exception.set_backtrace(caller.unshift line)
-
end
-
end
-
-
1
def notify(*args)
-
RSpec::Support.notify_failure(*args)
-
end
-
-
1
def format_args(args)
-
return "(no args)" if args.empty?
-
"(#{arg_list(args)})"
-
end
-
-
1
def arg_list(args)
-
args.map { |arg| RSpec::Support::ObjectFormatter.format(arg) }.join(", ")
-
end
-
-
1
def format_received_args(args_for_multiple_calls)
-
grouped_args(args_for_multiple_calls).map do |args_for_one_call, index|
-
"#{format_args(args_for_one_call)}#{group_count(index, args_for_multiple_calls)}"
-
end.join("\n ")
-
end
-
-
1
def count_message(count, expectation_count_type=nil)
-
return "at least #{times(count.abs)}" if count < 0 || expectation_count_type == :at_least
-
return "at most #{times(count)}" if expectation_count_type == :at_most
-
times(count)
-
end
-
-
1
def times(count)
-
"#{count} time#{count == 1 ? '' : 's'}"
-
end
-
-
1
def grouped_args(args)
-
Hash[args.group_by { |x| x }.map { |k, v| [k, v.count] }]
-
end
-
-
1
def group_count(index, args)
-
" (#{times(index)})" if args.size > 1 || index > 1
-
end
-
end
-
-
# @private
-
1
def self.error_generator
-
@error_generator ||= ErrorGenerator.new
-
end
-
end
-
end
-
1
RSpec::Support.require_rspec_mocks 'object_reference'
-
-
1
module RSpec
-
1
module Mocks
-
# Contains methods intended to be used from within code examples.
-
# Mix this in to your test context (such as a test framework base class)
-
# to use rspec-mocks with your test framework. If you're using rspec-core,
-
# it'll take care of doing this for you.
-
1
module ExampleMethods
-
1
include RSpec::Mocks::ArgumentMatchers
-
-
# @overload double()
-
# @overload double(name)
-
# @param name [String/Symbol] name or description to be used in failure messages
-
# @overload double(stubs)
-
# @param stubs (Hash) hash of message/return-value pairs
-
# @overload double(name, stubs)
-
# @param name [String/Symbol] name or description to be used in failure messages
-
# @param stubs (Hash) hash of message/return-value pairs
-
# @return (Double)
-
#
-
# Constructs an instance of [RSpec::Mocks::Double](RSpec::Mocks::Double) configured
-
# with an optional name, used for reporting in failure messages, and an optional
-
# hash of message/return-value pairs.
-
#
-
# @example
-
# book = double("book", :title => "The RSpec Book")
-
# book.title #=> "The RSpec Book"
-
#
-
# card = double("card", :suit => "Spades", :rank => "A")
-
# card.suit #=> "Spades"
-
# card.rank #=> "A"
-
#
-
1
def double(*args)
-
ExampleMethods.declare_double(Double, *args)
-
end
-
-
# @overload instance_double(doubled_class)
-
# @param doubled_class [String, Class]
-
# @overload instance_double(doubled_class, name)
-
# @param doubled_class [String, Class]
-
# @param name [String/Symbol] name or description to be used in failure messages
-
# @overload instance_double(doubled_class, stubs)
-
# @param doubled_class [String, Class]
-
# @param stubs [Hash] hash of message/return-value pairs
-
# @overload instance_double(doubled_class, name, stubs)
-
# @param doubled_class [String, Class]
-
# @param name [String/Symbol] name or description to be used in failure messages
-
# @param stubs [Hash] hash of message/return-value pairs
-
# @return InstanceVerifyingDouble
-
#
-
# Constructs a test double against a specific class. If the given class
-
# name has been loaded, only instance methods defined on the class are
-
# allowed to be stubbed. In all other ways it behaves like a
-
# [double](double).
-
1
def instance_double(doubled_class, *args)
-
ref = ObjectReference.for(doubled_class)
-
ExampleMethods.declare_verifying_double(InstanceVerifyingDouble, ref, *args)
-
end
-
-
# @overload class_double(doubled_class)
-
# @param doubled_class [String, Module]
-
# @overload class_double(doubled_class, name)
-
# @param doubled_class [String, Module]
-
# @param name [String/Symbol] name or description to be used in failure messages
-
# @overload class_double(doubled_class, stubs)
-
# @param doubled_class [String, Module]
-
# @param stubs [Hash] hash of message/return-value pairs
-
# @overload class_double(doubled_class, name, stubs)
-
# @param doubled_class [String, Module]
-
# @param name [String/Symbol] name or description to be used in failure messages
-
# @param stubs [Hash] hash of message/return-value pairs
-
# @return ClassVerifyingDouble
-
#
-
# Constructs a test double against a specific class. If the given class
-
# name has been loaded, only class methods defined on the class are
-
# allowed to be stubbed. In all other ways it behaves like a
-
# [double](double).
-
1
def class_double(doubled_class, *args)
-
ref = ObjectReference.for(doubled_class)
-
ExampleMethods.declare_verifying_double(ClassVerifyingDouble, ref, *args)
-
end
-
-
# @overload object_double(object_or_name)
-
# @param object_or_name [String, Object]
-
# @overload object_double(object_or_name, name)
-
# @param object_or_name [String, Object]
-
# @param name [String/Symbol] name or description to be used in failure messages
-
# @overload object_double(object_or_name, stubs)
-
# @param object_or_name [String, Object]
-
# @param stubs [Hash] hash of message/return-value pairs
-
# @overload object_double(object_or_name, name, stubs)
-
# @param object_or_name [String, Object]
-
# @param name [String/Symbol] name or description to be used in failure messages
-
# @param stubs [Hash] hash of message/return-value pairs
-
# @return ObjectVerifyingDouble
-
#
-
# Constructs a test double against a specific object. Only the methods
-
# the object responds to are allowed to be stubbed. If a String argument
-
# is provided, it is assumed to reference a constant object which is used
-
# for verification. In all other ways it behaves like a [double](double).
-
1
def object_double(object_or_name, *args)
-
ref = ObjectReference.for(object_or_name, :allow_direct_object_refs)
-
ExampleMethods.declare_verifying_double(ObjectVerifyingDouble, ref, *args)
-
end
-
-
# @overload spy()
-
# @overload spy(name)
-
# @param name [String/Symbol] name or description to be used in failure messages
-
# @overload spy(stubs)
-
# @param stubs (Hash) hash of message/return-value pairs
-
# @overload spy(name, stubs)
-
# @param name [String/Symbol] name or description to be used in failure messages
-
# @param stubs (Hash) hash of message/return-value pairs
-
# @return (Double)
-
#
-
# Constructs a test double that is optimized for use with
-
# `have_received`. With a normal double one has to stub methods in order
-
# to be able to spy them. A spy automatically spies on all methods.
-
1
def spy(*args)
-
double(*args).as_null_object
-
end
-
-
# @overload instance_spy(doubled_class)
-
# @param doubled_class [String, Class]
-
# @overload instance_spy(doubled_class, name)
-
# @param doubled_class [String, Class]
-
# @param name [String/Symbol] name or description to be used in failure messages
-
# @overload instance_spy(doubled_class, stubs)
-
# @param doubled_class [String, Class]
-
# @param stubs [Hash] hash of message/return-value pairs
-
# @overload instance_spy(doubled_class, name, stubs)
-
# @param doubled_class [String, Class]
-
# @param name [String/Symbol] name or description to be used in failure messages
-
# @param stubs [Hash] hash of message/return-value pairs
-
# @return InstanceVerifyingDouble
-
#
-
# Constructs a test double that is optimized for use with `have_received`
-
# against a specific class. If the given class name has been loaded, only
-
# instance methods defined on the class are allowed to be stubbed. With
-
# a normal double one has to stub methods in order to be able to spy
-
# them. An instance_spy automatically spies on all instance methods to
-
# which the class responds.
-
1
def instance_spy(*args)
-
instance_double(*args).as_null_object
-
end
-
-
# @overload object_spy(object_or_name)
-
# @param object_or_name [String, Object]
-
# @overload object_spy(object_or_name, name)
-
# @param object_or_name [String, Class]
-
# @param name [String/Symbol] name or description to be used in failure messages
-
# @overload object_spy(object_or_name, stubs)
-
# @param object_or_name [String, Object]
-
# @param stubs [Hash] hash of message/return-value pairs
-
# @overload object_spy(object_or_name, name, stubs)
-
# @param object_or_name [String, Class]
-
# @param name [String/Symbol] name or description to be used in failure messages
-
# @param stubs [Hash] hash of message/return-value pairs
-
# @return ObjectVerifyingDouble
-
#
-
# Constructs a test double that is optimized for use with `have_received`
-
# against a specific object. Only instance methods defined on the object
-
# are allowed to be stubbed. With a normal double one has to stub
-
# methods in order to be able to spy them. An object_spy automatically
-
# spies on all methods to which the object responds.
-
1
def object_spy(*args)
-
object_double(*args).as_null_object
-
end
-
-
# @overload class_spy(doubled_class)
-
# @param doubled_class [String, Module]
-
# @overload class_spy(doubled_class, name)
-
# @param doubled_class [String, Class]
-
# @param name [String/Symbol] name or description to be used in failure messages
-
# @overload class_spy(doubled_class, stubs)
-
# @param doubled_class [String, Module]
-
# @param stubs [Hash] hash of message/return-value pairs
-
# @overload class_spy(doubled_class, name, stubs)
-
# @param doubled_class [String, Class]
-
# @param name [String/Symbol] name or description to be used in failure messages
-
# @param stubs [Hash] hash of message/return-value pairs
-
# @return ClassVerifyingDouble
-
#
-
# Constructs a test double that is optimized for use with `have_received`
-
# against a specific class. If the given class name has been loaded,
-
# only class methods defined on the class are allowed to be stubbed.
-
# With a normal double one has to stub methods in order to be able to spy
-
# them. An class_spy automatically spies on all class methods to which the
-
# class responds.
-
1
def class_spy(*args)
-
class_double(*args).as_null_object
-
end
-
-
# Disables warning messages about expectations being set on nil.
-
#
-
# By default warning messages are issued when expectations are set on
-
# nil. This is to prevent false-positives and to catch potential bugs
-
# early on.
-
1
def allow_message_expectations_on_nil
-
RSpec::Mocks.space.proxy_for(nil).warn_about_expectations = false
-
end
-
-
# Stubs the named constant with the given value.
-
# Like method stubs, the constant will be restored
-
# to its original value (or lack of one, if it was
-
# undefined) when the example completes.
-
#
-
# @param constant_name [String] The fully qualified name of the constant. The current
-
# constant scoping at the point of call is not considered.
-
# @param value [Object] The value to make the constant refer to. When the
-
# example completes, the constant will be restored to its prior state.
-
# @param options [Hash] Stubbing options.
-
# @option options :transfer_nested_constants [Boolean, Array<Symbol>] Determines
-
# what nested constants, if any, will be transferred from the original value
-
# of the constant to the new value of the constant. This only works if both
-
# the original and new values are modules (or classes).
-
# @return [Object] the stubbed value of the constant
-
#
-
# @example
-
# stub_const("MyClass", Class.new) # => Replaces (or defines) MyClass with a new class object.
-
# stub_const("SomeModel::PER_PAGE", 5) # => Sets SomeModel::PER_PAGE to 5.
-
#
-
# class CardDeck
-
# SUITS = [:Spades, :Diamonds, :Clubs, :Hearts]
-
# NUM_CARDS = 52
-
# end
-
#
-
# stub_const("CardDeck", Class.new)
-
# CardDeck::SUITS # => uninitialized constant error
-
# CardDeck::NUM_CARDS # => uninitialized constant error
-
#
-
# stub_const("CardDeck", Class.new, :transfer_nested_constants => true)
-
# CardDeck::SUITS # => our suits array
-
# CardDeck::NUM_CARDS # => 52
-
#
-
# stub_const("CardDeck", Class.new, :transfer_nested_constants => [:SUITS])
-
# CardDeck::SUITS # => our suits array
-
# CardDeck::NUM_CARDS # => uninitialized constant error
-
1
def stub_const(constant_name, value, options={})
-
ConstantMutator.stub(constant_name, value, options)
-
end
-
-
# Hides the named constant with the given value. The constant will be
-
# undefined for the duration of the test.
-
#
-
# Like method stubs, the constant will be restored to its original value
-
# when the example completes.
-
#
-
# @param constant_name [String] The fully qualified name of the constant.
-
# The current constant scoping at the point of call is not considered.
-
#
-
# @example
-
# hide_const("MyClass") # => MyClass is now an undefined constant
-
1
def hide_const(constant_name)
-
ConstantMutator.hide(constant_name)
-
end
-
-
# Verifies that the given object received the expected message during the
-
# course of the test. On a spy objects or as null object doubles this
-
# works for any method, on other objects the method must have
-
# been stubbed beforehand in order for messages to be verified.
-
#
-
# Stubbing and verifying messages received in this way implements the
-
# Test Spy pattern.
-
#
-
# @param method_name [Symbol] name of the method expected to have been
-
# called.
-
#
-
# @example
-
# invitation = double('invitation', accept: true)
-
# user.accept_invitation(invitation)
-
# expect(invitation).to have_received(:accept)
-
#
-
# # You can also use most message expectations:
-
# expect(invitation).to have_received(:accept).with(mailer).once
-
#
-
# @note `have_received(...).with(...)` is unable to work properly when
-
# passed arguments are mutated after the spy records the received message.
-
1
def have_received(method_name, &block)
-
Matchers::HaveReceived.new(method_name, &block)
-
end
-
-
# @method expect
-
# Used to wrap an object in preparation for setting a mock expectation
-
# on it.
-
#
-
# @example
-
# expect(obj).to receive(:foo).with(5).and_return(:return_value)
-
#
-
# @note This method is usually provided by rspec-expectations. However,
-
# if you use rspec-mocks without rspec-expectations, there's a definition
-
# of it that is made available here. If you disable the `:expect` syntax
-
# this method will be undefined.
-
-
# @method allow
-
# Used to wrap an object in preparation for stubbing a method
-
# on it.
-
#
-
# @example
-
# allow(dbl).to receive(:foo).with(5).and_return(:return_value)
-
#
-
# @note If you disable the `:expect` syntax this method will be undefined.
-
-
# @method expect_any_instance_of
-
# Used to wrap a class in preparation for setting a mock expectation
-
# on instances of it.
-
#
-
# @example
-
# expect_any_instance_of(MyClass).to receive(:foo)
-
#
-
# @note If you disable the `:expect` syntax this method will be undefined.
-
-
# @method allow_any_instance_of
-
# Used to wrap a class in preparation for stubbing a method
-
# on instances of it.
-
#
-
# @example
-
# allow_any_instance_of(MyClass).to receive(:foo)
-
#
-
# @note This is only available when you have enabled the `expect` syntax.
-
-
# @method receive
-
# Used to specify a message that you expect or allow an object
-
# to receive. The object returned by `receive` supports the same
-
# fluent interface that `should_receive` and `stub` have always
-
# supported, allowing you to constrain the arguments or number of
-
# times, and configure how the object should respond to the message.
-
#
-
# @example
-
# expect(obj).to receive(:hello).with("world").exactly(3).times
-
#
-
# @note If you disable the `:expect` syntax this method will be undefined.
-
-
# @method receive_messages
-
# Shorthand syntax used to setup message(s), and their return value(s),
-
# that you expect or allow an object to receive. The method takes a hash
-
# of messages and their respective return values. Unlike with `receive`,
-
# you cannot apply further customizations using a block or the fluent
-
# interface.
-
#
-
# @example
-
# allow(obj).to receive_messages(:speak => "Hello World")
-
# allow(obj).to receive_messages(:speak => "Hello", :meow => "Meow")
-
#
-
# @note If you disable the `:expect` syntax this method will be undefined.
-
-
# @method receive_message_chain
-
# @overload receive_message_chain(method1, method2)
-
# @overload receive_message_chain("method1.method2")
-
# @overload receive_message_chain(method1, method_to_value_hash)
-
#
-
# stubs/mocks a chain of messages on an object or test double.
-
#
-
# ## Warning:
-
#
-
# Chains can be arbitrarily long, which makes it quite painless to
-
# violate the Law of Demeter in violent ways, so you should consider any
-
# use of `receive_message_chain` a code smell. Even though not all code smells
-
# indicate real problems (think fluent interfaces), `receive_message_chain` still
-
# results in brittle examples. For example, if you write
-
# `allow(foo).to receive_message_chain(:bar, :baz => 37)` in a spec and then the
-
# implementation calls `foo.baz.bar`, the stub will not work.
-
#
-
# @example
-
# allow(double).to receive_message_chain("foo.bar") { :baz }
-
# allow(double).to receive_message_chain(:foo, :bar => :baz)
-
# allow(double).to receive_message_chain(:foo, :bar) { :baz }
-
#
-
# # Given any of ^^ these three forms ^^:
-
# double.foo.bar # => :baz
-
#
-
# # Common use in Rails/ActiveRecord:
-
# allow(Article).to receive_message_chain("recent.published") { [Article.new] }
-
#
-
# @note If you disable the `:expect` syntax this method will be undefined.
-
-
# @private
-
1
def self.included(klass)
-
1
klass.class_exec do
-
# This gets mixed in so that if `RSpec::Matchers` is included in
-
# `klass` later, it's definition of `expect` will take precedence.
-
1
include ExpectHost unless method_defined?(:expect)
-
end
-
end
-
-
# @private
-
1
def self.extended(object)
-
# This gets extended in so that if `RSpec::Matchers` is included in
-
# `klass` later, it's definition of `expect` will take precedence.
-
object.extend ExpectHost unless object.respond_to?(:expect)
-
end
-
-
# @private
-
1
def self.declare_verifying_double(type, ref, *args)
-
if RSpec::Mocks.configuration.verify_doubled_constant_names? &&
-
!ref.defined?
-
-
RSpec::Mocks.error_generator.raise_verifying_double_not_defined_error(ref)
-
end
-
-
RSpec::Mocks.configuration.verifying_double_callbacks.each do |block|
-
block.call(ref)
-
end
-
-
declare_double(type, ref, *args)
-
end
-
-
# @private
-
1
def self.declare_double(type, *args)
-
args << {} unless Hash === args.last
-
type.new(*args)
-
end
-
-
# This module exists to host the `expect` method for cases where
-
# rspec-mocks is used w/o rspec-expectations.
-
1
module ExpectHost
-
end
-
end
-
end
-
end
-
1
module RSpec
-
1
module Mocks
-
# @private
-
1
class InstanceMethodStasher
-
1
def initialize(object, method)
-
@object = object
-
@method = method
-
@klass = (class << object; self; end)
-
-
@original_method = nil
-
@method_is_stashed = false
-
end
-
-
1
attr_reader :original_method
-
-
1
if RUBY_VERSION.to_f < 1.9
-
# @private
-
def method_is_stashed?
-
@method_is_stashed
-
end
-
-
# @private
-
def stash
-
return if !method_defined_directly_on_klass? || @method_is_stashed
-
-
@klass.__send__(:alias_method, stashed_method_name, @method)
-
@method_is_stashed = true
-
end
-
-
# @private
-
def stashed_method_name
-
"obfuscated_by_rspec_mocks__#{@method}"
-
end
-
private :stashed_method_name
-
-
# @private
-
def restore
-
return unless @method_is_stashed
-
-
if @klass.__send__(:method_defined?, @method)
-
@klass.__send__(:undef_method, @method)
-
end
-
@klass.__send__(:alias_method, @method, stashed_method_name)
-
@klass.__send__(:remove_method, stashed_method_name)
-
@method_is_stashed = false
-
end
-
else
-
-
# @private
-
1
def method_is_stashed?
-
!!@original_method
-
end
-
-
# @private
-
1
def stash
-
return unless method_defined_directly_on_klass?
-
@original_method ||= ::RSpec::Support.method_handle_for(@object, @method)
-
end
-
-
# @private
-
1
def restore
-
return unless @original_method
-
-
if @klass.__send__(:method_defined?, @method)
-
@klass.__send__(:undef_method, @method)
-
end
-
-
handle_restoration_failures do
-
@klass.__send__(:define_method, @method, @original_method)
-
end
-
-
@original_method = nil
-
end
-
end
-
-
1
if RUBY_DESCRIPTION.include?('2.0.0p247') || RUBY_DESCRIPTION.include?('2.0.0p195')
-
# ruby 2.0.0-p247 and 2.0.0-p195 both have a bug that we can't work around :(.
-
# https://bugs.ruby-lang.org/issues/8686
-
def handle_restoration_failures
-
yield
-
rescue TypeError
-
RSpec.warn_with(
-
"RSpec failed to properly restore a partial double (#{@object.inspect}) " \
-
"to its original state due to a known bug in MRI 2.0.0-p195 & p247 " \
-
"(https://bugs.ruby-lang.org/issues/8686). This object may remain " \
-
"screwed up for the rest of this process. Please upgrade to 2.0.0-p353 or above.",
-
:call_site => nil, :use_spec_location_as_call_site => true
-
)
-
end
-
else
-
1
def handle_restoration_failures
-
# No known reasons for restoration to fail on other rubies.
-
yield
-
end
-
end
-
-
1
private
-
-
# @private
-
1
def method_defined_directly_on_klass?
-
method_defined_on_klass? && method_owned_by_klass?
-
end
-
-
# @private
-
1
def method_defined_on_klass?(klass=@klass)
-
MethodReference.method_defined_at_any_visibility?(klass, @method)
-
end
-
-
1
def method_owned_by_klass?
-
owner = @klass.instance_method(@method).owner
-
-
# On Ruby 2.0.0+ the owner of a method on a class which has been
-
# `prepend`ed may actually be an instance, e.g.
-
# `#<MyClass:0x007fbb94e3cd10>`, rather than the expected `MyClass`.
-
owner = owner.class unless Module === owner
-
-
# On some 1.9s (e.g. rubinius) aliased methods
-
# can report the wrong owner. Example:
-
# class MyClass
-
# class << self
-
# alias alternate_new new
-
# end
-
# end
-
#
-
# MyClass.owner(:alternate_new) returns `Class` when incorrect,
-
# but we need to consider the owner to be `MyClass` because
-
# it is not actually available on `Class` but is on `MyClass`.
-
# Hence, we verify that the owner actually has the method defined.
-
# If the given owner does not have the method defined, we assume
-
# that the method is actually owned by @klass.
-
owner == @klass || !(method_defined_on_klass?(owner))
-
end
-
end
-
end
-
end
-
1
module RSpec
-
1
module Mocks
-
# A message expectation that only allows concrete return values to be set
-
# for a message. While this same effect can be achieved using a standard
-
# MessageExpecation, this version is much faster and so can be used as an
-
# optimization.
-
#
-
# @private
-
1
class SimpleMessageExpectation
-
1
def initialize(message, response, error_generator, backtrace_line=nil)
-
@message, @response, @error_generator, @backtrace_line = message.to_sym, response, error_generator, backtrace_line
-
@received = false
-
end
-
-
1
def invoke(*_)
-
@received = true
-
@response
-
end
-
-
1
def matches?(message, *_)
-
@message == message.to_sym
-
end
-
-
1
def called_max_times?
-
false
-
end
-
-
1
def verify_messages_received
-
return if @received
-
@error_generator.raise_expectation_error(
-
@message, 1, ArgumentListMatcher::MATCH_ALL, 0, nil, [], @backtrace_line
-
)
-
end
-
-
1
def unadvise(_)
-
end
-
end
-
-
# Represents an individual method stub or message expectation. The methods
-
# defined here can be used to configure how it behaves. The methods return
-
# `self` so that they can be chained together to form a fluent interface.
-
1
class MessageExpectation
-
# @!group Configuring Responses
-
-
# @overload and_return(value)
-
# @overload and_return(first_value, second_value)
-
#
-
# Tells the object to return a value when it receives the message. Given
-
# more than one value, the first value is returned the first time the
-
# message is received, the second value is returned the next time, etc,
-
# etc.
-
#
-
# If the message is received more times than there are values, the last
-
# value is received for every subsequent call.
-
#
-
# @return [nil] No further chaining is supported after this.
-
# @example
-
# allow(counter).to receive(:count).and_return(1)
-
# counter.count # => 1
-
# counter.count # => 1
-
#
-
# allow(counter).to receive(:count).and_return(1,2,3)
-
# counter.count # => 1
-
# counter.count # => 2
-
# counter.count # => 3
-
# counter.count # => 3
-
# counter.count # => 3
-
# # etc
-
1
def and_return(first_value, *values)
-
raise_already_invoked_error_if_necessary(__method__)
-
if negative?
-
raise "`and_return` is not supported with negative message expectations"
-
end
-
-
if block_given?
-
raise ArgumentError, "Implementation blocks aren't supported with `and_return`"
-
end
-
-
values.unshift(first_value)
-
@expected_received_count = [@expected_received_count, values.size].max unless ignoring_args? || (@expected_received_count == 0 && @at_least)
-
self.terminal_implementation_action = AndReturnImplementation.new(values)
-
-
nil
-
end
-
-
# Tells the object to delegate to the original unmodified method
-
# when it receives the message.
-
#
-
# @note This is only available on partial doubles.
-
#
-
# @return [nil] No further chaining is supported after this.
-
# @example
-
# expect(counter).to receive(:increment).and_call_original
-
# original_count = counter.count
-
# counter.increment
-
# expect(counter.count).to eq(original_count + 1)
-
1
def and_call_original
-
and_wrap_original do |original, *args, &block|
-
original.call(*args, &block)
-
end
-
end
-
-
# Decorates the stubbed method with the supplied block. The original
-
# unmodified method is passed to the block along with any method call
-
# arguments so you can delegate to it, whilst still being able to
-
# change what args are passed to it and/or change the return value.
-
#
-
# @note This is only available on partial doubles.
-
#
-
# @return [nil] No further chaining is supported after this.
-
# @example
-
# expect(api).to receive(:large_list).and_wrap_original do |original_method, *args, &block|
-
# original_method.call(*args, &block).first(10)
-
# end
-
1
def and_wrap_original(&block)
-
if RSpec::Mocks::TestDouble === @method_double.object
-
@error_generator.raise_only_valid_on_a_partial_double(:and_call_original)
-
else
-
warn_about_stub_override if implementation.inner_action
-
@implementation = AndWrapOriginalImplementation.new(@method_double.original_implementation_callable, block)
-
@yield_receiver_to_implementation_block = false
-
end
-
-
nil
-
end
-
-
# @overload and_raise
-
# @overload and_raise(ExceptionClass)
-
# @overload and_raise(ExceptionClass, message)
-
# @overload and_raise(exception_instance)
-
#
-
# Tells the object to raise an exception when the message is received.
-
#
-
# @return [nil] No further chaining is supported after this.
-
# @note
-
# When you pass an exception class, the MessageExpectation will raise
-
# an instance of it, creating it with `exception` and passing `message`
-
# if specified. If the exception class initializer requires more than
-
# one parameters, you must pass in an instance and not the class,
-
# otherwise this method will raise an ArgumentError exception.
-
#
-
# @example
-
# allow(car).to receive(:go).and_raise
-
# allow(car).to receive(:go).and_raise(OutOfGas)
-
# allow(car).to receive(:go).and_raise(OutOfGas, "At least 2 oz of gas needed to drive")
-
# allow(car).to receive(:go).and_raise(OutOfGas.new(2, :oz))
-
1
def and_raise(*args)
-
raise_already_invoked_error_if_necessary(__method__)
-
self.terminal_implementation_action = Proc.new { raise(*args) }
-
nil
-
end
-
-
# @overload and_throw(symbol)
-
# @overload and_throw(symbol, object)
-
#
-
# Tells the object to throw a symbol (with the object if that form is
-
# used) when the message is received.
-
#
-
# @return [nil] No further chaining is supported after this.
-
# @example
-
# allow(car).to receive(:go).and_throw(:out_of_gas)
-
# allow(car).to receive(:go).and_throw(:out_of_gas, :level => 0.1)
-
1
def and_throw(*args)
-
raise_already_invoked_error_if_necessary(__method__)
-
self.terminal_implementation_action = Proc.new { throw(*args) }
-
nil
-
end
-
-
# Tells the object to yield one or more args to a block when the message
-
# is received.
-
#
-
# @return [MessageExpecation] self, to support further chaining.
-
# @example
-
# stream.stub(:open).and_yield(StringIO.new)
-
1
def and_yield(*args, &block)
-
raise_already_invoked_error_if_necessary(__method__)
-
yield @eval_context = Object.new if block
-
-
# Initialize args to yield now that it's being used, see also: comment
-
# in constructor.
-
@args_to_yield ||= []
-
-
@args_to_yield << args
-
self.initial_implementation_action = AndYieldImplementation.new(@args_to_yield, @eval_context, @error_generator)
-
self
-
end
-
# @!endgroup
-
-
# @!group Constraining Receive Counts
-
-
# Constrain a message expectation to be received a specific number of
-
# times.
-
#
-
# @return [MessageExpecation] self, to support further chaining.
-
# @example
-
# expect(dealer).to receive(:deal_card).exactly(10).times
-
1
def exactly(n, &block)
-
raise_already_invoked_error_if_necessary(__method__)
-
self.inner_implementation_action = block
-
set_expected_received_count :exactly, n
-
self
-
end
-
-
# Constrain a message expectation to be received at least a specific
-
# number of times.
-
#
-
# @return [MessageExpecation] self, to support further chaining.
-
# @example
-
# expect(dealer).to receive(:deal_card).at_least(9).times
-
1
def at_least(n, &block)
-
raise_already_invoked_error_if_necessary(__method__)
-
set_expected_received_count :at_least, n
-
-
if n == 0
-
raise "at_least(0) has been removed, use allow(...).to receive(:message) instead"
-
end
-
-
self.inner_implementation_action = block
-
-
self
-
end
-
-
# Constrain a message expectation to be received at most a specific
-
# number of times.
-
#
-
# @return [MessageExpecation] self, to support further chaining.
-
# @example
-
# expect(dealer).to receive(:deal_card).at_most(10).times
-
1
def at_most(n, &block)
-
raise_already_invoked_error_if_necessary(__method__)
-
self.inner_implementation_action = block
-
set_expected_received_count :at_most, n
-
self
-
end
-
-
# Syntactic sugar for `exactly`, `at_least` and `at_most`
-
#
-
# @return [MessageExpecation] self, to support further chaining.
-
# @example
-
# expect(dealer).to receive(:deal_card).exactly(10).times
-
# expect(dealer).to receive(:deal_card).at_least(10).times
-
# expect(dealer).to receive(:deal_card).at_most(10).times
-
1
def times(&block)
-
self.inner_implementation_action = block
-
self
-
end
-
-
# Expect a message not to be received at all.
-
#
-
# @return [MessageExpecation] self, to support further chaining.
-
# @example
-
# expect(car).to receive(:stop).never
-
1
def never
-
error_generator.raise_double_negation_error("expect(obj)") if negative?
-
@expected_received_count = 0
-
self
-
end
-
-
# Expect a message to be received exactly one time.
-
#
-
# @return [MessageExpecation] self, to support further chaining.
-
# @example
-
# expect(car).to receive(:go).once
-
1
def once(&block)
-
self.inner_implementation_action = block
-
set_expected_received_count :exactly, 1
-
self
-
end
-
-
# Expect a message to be received exactly two times.
-
#
-
# @return [MessageExpecation] self, to support further chaining.
-
# @example
-
# expect(car).to receive(:go).twice
-
1
def twice(&block)
-
self.inner_implementation_action = block
-
set_expected_received_count :exactly, 2
-
self
-
end
-
-
# Expect a message to be received exactly three times.
-
#
-
# @return [MessageExpecation] self, to support further chaining.
-
# @example
-
# expect(car).to receive(:go).thrice
-
1
def thrice(&block)
-
self.inner_implementation_action = block
-
set_expected_received_count :exactly, 3
-
self
-
end
-
# @!endgroup
-
-
# @!group Other Constraints
-
-
# Constrains a stub or message expectation to invocations with specific
-
# arguments.
-
#
-
# With a stub, if the message might be received with other args as well,
-
# you should stub a default value first, and then stub or mock the same
-
# message using `with` to constrain to specific arguments.
-
#
-
# A message expectation will fail if the message is received with different
-
# arguments.
-
#
-
# @return [MessageExpecation] self, to support further chaining.
-
# @example
-
# allow(cart).to receive(:add) { :failure }
-
# allow(cart).to receive(:add).with(Book.new(:isbn => 1934356379)) { :success }
-
# cart.add(Book.new(:isbn => 1234567890))
-
# # => :failure
-
# cart.add(Book.new(:isbn => 1934356379))
-
# # => :success
-
#
-
# expect(cart).to receive(:add).with(Book.new(:isbn => 1934356379)) { :success }
-
# cart.add(Book.new(:isbn => 1234567890))
-
# # => failed expectation
-
# cart.add(Book.new(:isbn => 1934356379))
-
# # => passes
-
1
def with(*args, &block)
-
raise_already_invoked_error_if_necessary(__method__)
-
if args.empty?
-
raise ArgumentError,
-
"`with` must have at least one argument. Use `no_args` matcher to set the expectation of receiving no arguments."
-
end
-
-
self.inner_implementation_action = block
-
@argument_list_matcher = ArgumentListMatcher.new(*args)
-
self
-
end
-
-
# Expect messages to be received in a specific order.
-
#
-
# @return [MessageExpecation] self, to support further chaining.
-
# @example
-
# expect(api).to receive(:prepare).ordered
-
# expect(api).to receive(:run).ordered
-
# expect(api).to receive(:finish).ordered
-
1
def ordered(&block)
-
self.inner_implementation_action = block
-
additional_expected_calls.times do
-
@order_group.register(self)
-
end
-
@ordered = true
-
self
-
end
-
-
# @private
-
# Contains the parts of `MessageExpecation` that aren't part of
-
# rspec-mocks' public API. The class is very big and could really use
-
# some collaborators it delegates to for this stuff but for now this was
-
# the simplest way to split the public from private stuff to make it
-
# easier to publish the docs for the APIs we want published.
-
1
module ImplementationDetails
-
1
attr_accessor :error_generator, :implementation
-
1
attr_reader :message
-
1
attr_reader :orig_object
-
1
attr_writer :expected_received_count, :expected_from, :argument_list_matcher
-
1
protected :expected_received_count=, :expected_from=, :error_generator, :error_generator=, :implementation=
-
-
# rubocop:disable Style/ParameterLists
-
1
def initialize(error_generator, expectation_ordering, expected_from, method_double,
-
type=:expectation, opts={}, &implementation_block)
-
@error_generator = error_generator
-
@error_generator.opts = opts
-
@expected_from = expected_from
-
@method_double = method_double
-
@orig_object = @method_double.object
-
@message = @method_double.method_name
-
@actual_received_count = 0
-
@expected_received_count = type == :expectation ? 1 : :any
-
@argument_list_matcher = ArgumentListMatcher::MATCH_ALL
-
@order_group = expectation_ordering
-
@order_group.register(self) unless type == :stub
-
@expectation_type = type
-
@ordered = false
-
@at_least = @at_most = @exactly = nil
-
-
# Initialized to nil so that we don't allocate an array for every
-
# mock or stub. See also comment in `and_yield`.
-
@args_to_yield = nil
-
@eval_context = nil
-
@yield_receiver_to_implementation_block = false
-
-
@implementation = Implementation.new
-
self.inner_implementation_action = implementation_block
-
end
-
# rubocop:enable Style/ParameterLists
-
-
1
def expected_args
-
@argument_list_matcher.expected_args
-
end
-
-
1
def and_yield_receiver_to_implementation
-
@yield_receiver_to_implementation_block = true
-
self
-
end
-
-
1
def yield_receiver_to_implementation_block?
-
@yield_receiver_to_implementation_block
-
end
-
-
1
def matches?(message, *args)
-
@message == message && @argument_list_matcher.args_match?(*args)
-
end
-
-
1
def safe_invoke(parent_stub, *args, &block)
-
invoke_incrementing_actual_calls_by(1, false, parent_stub, *args, &block)
-
end
-
-
1
def invoke(parent_stub, *args, &block)
-
invoke_incrementing_actual_calls_by(1, true, parent_stub, *args, &block)
-
end
-
-
1
def invoke_without_incrementing_received_count(parent_stub, *args, &block)
-
invoke_incrementing_actual_calls_by(0, true, parent_stub, *args, &block)
-
end
-
-
1
def negative?
-
@expected_received_count == 0 && !@at_least
-
end
-
-
1
def called_max_times?
-
@expected_received_count != :any &&
-
!@at_least &&
-
@expected_received_count > 0 &&
-
@actual_received_count >= @expected_received_count
-
end
-
-
1
def matches_name_but_not_args(message, *args)
-
@message == message && !@argument_list_matcher.args_match?(*args)
-
end
-
-
1
def verify_messages_received
-
return if expected_messages_received?
-
generate_error
-
end
-
-
1
def expected_messages_received?
-
ignoring_args? || matches_exact_count? || matches_at_least_count? || matches_at_most_count?
-
end
-
-
1
def ensure_expected_ordering_received!
-
@order_group.verify_invocation_order(self) if @ordered
-
true
-
end
-
-
1
def ignoring_args?
-
@expected_received_count == :any
-
end
-
-
1
def matches_at_least_count?
-
@at_least && @actual_received_count >= @expected_received_count
-
end
-
-
1
def matches_at_most_count?
-
@at_most && @actual_received_count <= @expected_received_count
-
end
-
-
1
def matches_exact_count?
-
@expected_received_count == @actual_received_count
-
end
-
-
1
def similar_messages
-
@similar_messages ||= []
-
end
-
-
1
def advise(*args)
-
similar_messages << args
-
end
-
-
1
def unadvise(args)
-
similar_messages.delete_if { |message| args.include?(message) }
-
end
-
-
1
def generate_error
-
if similar_messages.empty?
-
@error_generator.raise_expectation_error(
-
@message, @expected_received_count, @argument_list_matcher,
-
@actual_received_count, expectation_count_type, expected_args,
-
@expected_from, exception_source_id
-
)
-
else
-
@error_generator.raise_similar_message_args_error(
-
self, @similar_messages, @expected_from
-
)
-
end
-
end
-
-
1
def raise_unexpected_message_args_error(args_for_multiple_calls)
-
@error_generator.raise_unexpected_message_args_error(self, args_for_multiple_calls, exception_source_id)
-
end
-
-
1
def expectation_count_type
-
return :at_least if @at_least
-
return :at_most if @at_most
-
nil
-
end
-
-
1
def description_for(verb)
-
@error_generator.describe_expectation(
-
verb, @message, @expected_received_count,
-
@actual_received_count, expected_args
-
)
-
end
-
-
1
def raise_out_of_order_error
-
@error_generator.raise_out_of_order_error @message
-
end
-
-
1
def additional_expected_calls
-
return 0 if @expectation_type == :stub || !@exactly
-
@expected_received_count - 1
-
end
-
-
1
def ordered?
-
@ordered
-
end
-
-
1
def negative_expectation_for?(message)
-
@message == message && negative?
-
end
-
-
1
def actual_received_count_matters?
-
@at_least || @at_most || @exactly
-
end
-
-
1
def increase_actual_received_count!
-
@actual_received_count += 1
-
end
-
-
1
private
-
-
1
def exception_source_id
-
@exception_source_id ||= "#{self.class.name} #{__id__}"
-
end
-
-
1
def invoke_incrementing_actual_calls_by(increment, allowed_to_fail, parent_stub, *args, &block)
-
args.unshift(orig_object) if yield_receiver_to_implementation_block?
-
-
if negative? || (allowed_to_fail && (@exactly || @at_most) && (@actual_received_count == @expected_received_count))
-
# args are the args we actually received, @argument_list_matcher is the
-
# list of args we were expecting
-
@error_generator.raise_expectation_error(
-
@message, @expected_received_count,
-
@argument_list_matcher,
-
@actual_received_count + increment,
-
expectation_count_type, args, nil, exception_source_id
-
)
-
end
-
-
@order_group.handle_order_constraint self
-
-
if implementation.present?
-
implementation.call(*args, &block)
-
elsif parent_stub
-
parent_stub.invoke(nil, *args, &block)
-
end
-
ensure
-
@actual_received_count += increment
-
end
-
-
1
def has_been_invoked?
-
@actual_received_count > 0
-
end
-
-
1
def raise_already_invoked_error_if_necessary(calling_customization)
-
return unless has_been_invoked?
-
-
error_generator.raise_already_invoked_error(message, calling_customization)
-
end
-
-
1
def set_expected_received_count(relativity, n)
-
@at_least = (relativity == :at_least)
-
@at_most = (relativity == :at_most)
-
@exactly = (relativity == :exactly)
-
@expected_received_count = case n
-
when Numeric then n
-
when :once then 1
-
when :twice then 2
-
when :thrice then 3
-
end
-
end
-
-
1
def initial_implementation_action=(action)
-
implementation.initial_action = action
-
end
-
-
1
def inner_implementation_action=(action)
-
return unless action
-
warn_about_stub_override if implementation.inner_action
-
implementation.inner_action = action
-
end
-
-
1
def terminal_implementation_action=(action)
-
implementation.terminal_action = action
-
end
-
-
1
def warn_about_stub_override
-
RSpec.warning(
-
"You're overriding a previous stub implementation of `#{@message}`. " \
-
"Called from #{CallerFilter.first_non_rspec_line}."
-
)
-
end
-
end
-
-
1
include ImplementationDetails
-
end
-
-
# Handles the implementation of an `and_yield` declaration.
-
# @private
-
1
class AndYieldImplementation
-
1
def initialize(args_to_yield, eval_context, error_generator)
-
@args_to_yield = args_to_yield
-
@eval_context = eval_context
-
@error_generator = error_generator
-
end
-
-
1
def call(*_args_to_ignore, &block)
-
return if @args_to_yield.empty? && @eval_context.nil?
-
-
@error_generator.raise_missing_block_error @args_to_yield unless block
-
value = nil
-
block_signature = Support::BlockSignature.new(block)
-
-
@args_to_yield.each do |args|
-
unless Support::StrictSignatureVerifier.new(block_signature, args).valid?
-
@error_generator.raise_wrong_arity_error(args, block_signature)
-
end
-
-
value = @eval_context ? @eval_context.instance_exec(*args, &block) : block.call(*args)
-
end
-
value
-
end
-
end
-
-
# Handles the implementation of an `and_return` implementation.
-
# @private
-
1
class AndReturnImplementation
-
1
def initialize(values_to_return)
-
@values_to_return = values_to_return
-
end
-
-
1
def call(*_args_to_ignore, &_block)
-
if @values_to_return.size > 1
-
@values_to_return.shift
-
else
-
@values_to_return.first
-
end
-
end
-
end
-
-
# Represents a configured implementation. Takes into account
-
# any number of sub-implementations.
-
# @private
-
1
class Implementation
-
1
attr_accessor :initial_action, :inner_action, :terminal_action
-
-
1
def call(*args, &block)
-
actions.map do |action|
-
action.call(*args, &block)
-
end.last
-
end
-
-
1
def present?
-
actions.any?
-
end
-
-
1
private
-
-
1
def actions
-
[initial_action, inner_action, terminal_action].compact
-
end
-
end
-
-
# Represents an `and_call_original` implementation.
-
# @private
-
1
class AndWrapOriginalImplementation
-
1
def initialize(method, block)
-
@method = method
-
@block = block
-
end
-
-
1
CannotModifyFurtherError = Class.new(StandardError)
-
-
1
def initial_action=(_value)
-
raise cannot_modify_further_error
-
end
-
-
1
def inner_action=(_value)
-
raise cannot_modify_further_error
-
end
-
-
1
def terminal_action=(_value)
-
raise cannot_modify_further_error
-
end
-
-
1
def present?
-
true
-
end
-
-
1
def inner_action
-
true
-
end
-
-
1
def call(*args, &block)
-
@block.call(@method, *args, &block)
-
end
-
-
1
private
-
-
1
def cannot_modify_further_error
-
CannotModifyFurtherError.new "This method has already been configured " \
-
"to call the original implementation, and cannot be modified further."
-
end
-
end
-
end
-
end
-
1
module RSpec
-
1
module Mocks
-
# @private
-
1
class MethodDouble
-
# @private
-
1
attr_reader :method_name, :object, :expectations, :stubs
-
-
# @private
-
1
def initialize(object, method_name, proxy)
-
@method_name = method_name
-
@object = object
-
@proxy = proxy
-
-
@original_visibility = nil
-
@method_stasher = InstanceMethodStasher.new(object, method_name)
-
@method_is_proxied = false
-
@expectations = []
-
@stubs = []
-
end
-
-
1
def original_implementation_callable
-
# If original method is not present, uses the `method_missing`
-
# handler of the object. This accounts for cases where the user has not
-
# correctly defined `respond_to?`, and also 1.8 which does not provide
-
# method handles for missing methods even if `respond_to?` is correct.
-
@original_implementation_callable ||= original_method ||
-
Proc.new do |*args, &block|
-
@object.__send__(:method_missing, @method_name, *args, &block)
-
end
-
end
-
-
1
alias_method :save_original_implementation_callable!, :original_implementation_callable
-
-
1
def original_method
-
@original_method ||=
-
@method_stasher.original_method ||
-
@proxy.original_method_handle_for(method_name)
-
end
-
-
# @private
-
1
def visibility
-
@proxy.visibility_for(@method_name)
-
end
-
-
# @private
-
1
def object_singleton_class
-
class << @object; self; end
-
end
-
-
# @private
-
1
def configure_method
-
@original_visibility = visibility
-
@method_stasher.stash unless @method_is_proxied
-
define_proxy_method
-
end
-
-
# @private
-
1
def define_proxy_method
-
return if @method_is_proxied
-
-
save_original_implementation_callable!
-
definition_target.class_exec(self, method_name, visibility) do |method_double, method_name, visibility|
-
define_method(method_name) do |*args, &block|
-
method_double.proxy_method_invoked(self, *args, &block)
-
end
-
__send__(visibility, method_name)
-
end
-
-
@method_is_proxied = true
-
end
-
-
# The implementation of the proxied method. Subclasses may override this
-
# method to perform additional operations.
-
#
-
# @private
-
1
def proxy_method_invoked(_obj, *args, &block)
-
@proxy.message_received method_name, *args, &block
-
end
-
-
# @private
-
1
def restore_original_method
-
return show_frozen_warning if object_singleton_class.frozen?
-
return unless @method_is_proxied
-
-
remove_method_from_definition_target
-
@method_stasher.restore if @method_stasher.method_is_stashed?
-
restore_original_visibility
-
-
@method_is_proxied = false
-
end
-
-
# @private
-
1
def show_frozen_warning
-
RSpec.warn_with(
-
"WARNING: rspec-mocks was unable to restore the original `#{@method_name}` " \
-
"method on #{@object.inspect} because it has been frozen. If you reuse this " \
-
"object, `#{@method_name}` will continue to respond with its stub implementation.",
-
:call_site => nil,
-
:use_spec_location_as_call_site => true
-
)
-
end
-
-
# @private
-
1
def restore_original_visibility
-
return unless @original_visibility &&
-
MethodReference.method_defined_at_any_visibility?(object_singleton_class, @method_name)
-
-
object_singleton_class.__send__(@original_visibility, method_name)
-
end
-
-
# @private
-
1
def verify
-
expectations.each { |e| e.verify_messages_received }
-
end
-
-
# @private
-
1
def reset
-
restore_original_method
-
clear
-
end
-
-
# @private
-
1
def clear
-
expectations.clear
-
stubs.clear
-
end
-
-
# The type of message expectation to create has been extracted to its own
-
# method so that subclasses can override it.
-
#
-
# @private
-
1
def message_expectation_class
-
MessageExpectation
-
end
-
-
# @private
-
1
def add_expectation(error_generator, expectation_ordering, expected_from, opts, &implementation)
-
configure_method
-
expectation = message_expectation_class.new(error_generator, expectation_ordering,
-
expected_from, self, :expectation, opts, &implementation)
-
expectations << expectation
-
expectation
-
end
-
-
# @private
-
1
def build_expectation(error_generator, expectation_ordering)
-
expected_from = IGNORED_BACKTRACE_LINE
-
message_expectation_class.new(error_generator, expectation_ordering, expected_from, self)
-
end
-
-
# @private
-
1
def add_stub(error_generator, expectation_ordering, expected_from, opts={}, &implementation)
-
configure_method
-
stub = message_expectation_class.new(error_generator, expectation_ordering, expected_from,
-
self, :stub, opts, &implementation)
-
stubs.unshift stub
-
stub
-
end
-
-
# A simple stub can only return a concrete value for a message, and
-
# cannot match on arguments. It is used as an optimization over
-
# `add_stub` / `add_expectation` where it is known in advance that this
-
# is all that will be required of a stub, such as when passing attributes
-
# to the `double` example method. They do not stash or restore existing method
-
# definitions.
-
#
-
# @private
-
1
def add_simple_stub(method_name, response)
-
setup_simple_method_double method_name, response, stubs
-
end
-
-
# @private
-
1
def add_simple_expectation(method_name, response, error_generator, backtrace_line)
-
setup_simple_method_double method_name, response, expectations, error_generator, backtrace_line
-
end
-
-
# @private
-
1
def setup_simple_method_double(method_name, response, collection, error_generator=nil, backtrace_line=nil)
-
define_proxy_method
-
-
me = SimpleMessageExpectation.new(method_name, response, error_generator, backtrace_line)
-
collection.unshift me
-
me
-
end
-
-
# @private
-
1
def add_default_stub(*args, &implementation)
-
return if stubs.any?
-
add_stub(*args, &implementation)
-
end
-
-
# @private
-
1
def remove_stub
-
raise_method_not_stubbed_error if stubs.empty?
-
remove_stub_if_present
-
end
-
-
# @private
-
1
def remove_stub_if_present
-
expectations.empty? ? reset : stubs.clear
-
end
-
-
# @private
-
1
def raise_method_not_stubbed_error
-
RSpec::Mocks.error_generator.raise_method_not_stubbed_error(method_name)
-
end
-
-
# In Ruby 2.0.0 and above prepend will alter the method lookup chain.
-
# We use an object's singleton class to define method doubles upon,
-
# however if the object has had it's singleton class (as opposed to
-
# it's actual class) prepended too then the the method lookup chain
-
# will look in the prepended module first, **before** the singleton
-
# class.
-
#
-
# This code works around that by providing a mock definition target
-
# that is either the singleton class, or if necessary, a prepended module
-
# of our own.
-
#
-
1
if Support::RubyFeatures.module_prepends_supported?
-
-
1
private
-
-
# We subclass `Module` in order to be able to easily detect our prepended module.
-
1
RSpecPrependedModule = Class.new(Module)
-
-
1
def definition_target
-
@definition_target ||= usable_rspec_prepended_module || object_singleton_class
-
end
-
-
1
def usable_rspec_prepended_module
-
@proxy.prepended_modules_of_singleton_class.each do |mod|
-
# If we have one of our modules prepended before one of the user's
-
# modules that defines the method, use that, since our module's
-
# definition will take precedence.
-
return mod if RSpecPrependedModule === mod
-
-
# If we hit a user module with the method defined first,
-
# we must create a new prepend module, even if one exists later,
-
# because ours will only take precedence if it comes first.
-
return new_rspec_prepended_module if mod.method_defined?(method_name)
-
end
-
-
nil
-
end
-
-
1
def new_rspec_prepended_module
-
RSpecPrependedModule.new.tap do |mod|
-
object_singleton_class.__send__ :prepend, mod
-
end
-
end
-
-
else
-
-
private
-
-
def definition_target
-
object_singleton_class
-
end
-
-
end
-
-
1
private
-
-
1
def remove_method_from_definition_target
-
definition_target.__send__(:remove_method, @method_name)
-
rescue NameError
-
# This can happen when the method has been monkeyed with by
-
# something outside RSpec. This happens, for example, when
-
# `file.write` has been stubbed, and then `file.reopen(other_io)`
-
# is later called, as `File#reopen` appears to redefine `write`.
-
#
-
# Note: we could avoid rescuing this by checking
-
# `definition_target.instance_method(@method_name).owner == definition_target`,
-
# saving us from the cost of the expensive exception, but this error is
-
# extremely rare (it was discovered on 2014-12-30, only happens on
-
# RUBY_VERSION < 2.0 and our spec suite only hits this condition once),
-
# so we'd rather avoid the cost of that check for every method double,
-
# and risk the rare situation where this exception will get raised.
-
RSpec.warn_with(
-
"WARNING: RSpec could not fully restore #{@object.inspect}." \
-
"#{@method_name}, possibly because the method has been redefined " \
-
"by something outside of RSpec."
-
)
-
end
-
end
-
end
-
end
-
1
module RSpec
-
1
module Mocks
-
# Represents a method on an object that may or may not be defined.
-
# The method may be an instance method on a module or a method on
-
# any object.
-
#
-
# @private
-
1
class MethodReference
-
1
def self.for(object_reference, method_name)
-
new(object_reference, method_name)
-
end
-
-
1
def initialize(object_reference, method_name)
-
@object_reference = object_reference
-
@method_name = method_name
-
end
-
-
# A method is implemented if sending the message does not result in
-
# a `NoMethodError`. It might be dynamically implemented by
-
# `method_missing`.
-
1
def implemented?
-
@object_reference.when_loaded do |m|
-
method_implemented?(m)
-
end
-
end
-
-
# Returns true if we definitively know that sending the method
-
# will result in a `NoMethodError`.
-
#
-
# This is not simply the inverse of `implemented?`: there are
-
# cases when we don't know if a method is implemented and
-
# both `implemented?` and `unimplemented?` will return false.
-
1
def unimplemented?
-
@object_reference.when_loaded do |_m|
-
return !implemented?
-
end
-
-
# If it's not loaded, then it may be implemented but we can't check.
-
false
-
end
-
-
# A method is defined if we are able to get a `Method` object for it.
-
# In that case, we can assert against metadata like the arity.
-
1
def defined?
-
@object_reference.when_loaded do |m|
-
method_defined?(m)
-
end
-
end
-
-
1
def with_signature
-
return unless (original = original_method)
-
yield Support::MethodSignature.new(original)
-
end
-
-
1
def visibility
-
@object_reference.when_loaded do |m|
-
return visibility_from(m)
-
end
-
-
# When it's not loaded, assume it's public. We don't want to
-
# wrongly treat the method as private.
-
:public
-
end
-
-
1
private
-
-
1
def original_method
-
@object_reference.when_loaded do |m|
-
self.defined? && find_method(m)
-
end
-
end
-
-
1
def self.instance_method_visibility_for(klass, method_name)
-
if klass.public_method_defined?(method_name)
-
:public
-
elsif klass.private_method_defined?(method_name)
-
:private
-
elsif klass.protected_method_defined?(method_name)
-
:protected
-
end
-
end
-
-
1
class << self
-
1
alias method_defined_at_any_visibility? instance_method_visibility_for
-
end
-
-
1
def self.method_visibility_for(object, method_name)
-
instance_method_visibility_for(class << object; self; end, method_name).tap do |vis|
-
# If the method is not defined on the class, `instance_method_visibility_for`
-
# returns `nil`. However, it may be handled dynamically by `method_missing`,
-
# so here we check `respond_to` (passing false to not check private methods).
-
#
-
# This only considers the public case, but I don't think it's possible to
-
# write `method_missing` in such a way that it handles a dynamic message
-
# with private or protected visibility. Ruby doesn't provide you with
-
# the caller info.
-
return :public if vis.nil? && object.respond_to?(method_name, false)
-
end
-
end
-
end
-
-
# @private
-
1
class InstanceMethodReference < MethodReference
-
1
private
-
-
1
def method_implemented?(mod)
-
MethodReference.method_defined_at_any_visibility?(mod, @method_name)
-
end
-
-
# Ideally, we'd use `respond_to?` for `method_implemented?` but we need a
-
# reference to an instance to do that and we don't have one. Note that
-
# we may get false negatives: if the method is implemented via
-
# `method_missing`, we'll return `false` even though it meets our
-
# definition of "implemented". However, it's the best we can do.
-
1
alias method_defined? method_implemented?
-
-
# works around the fact that repeated calls for method parameters will
-
# falsely return empty arrays on JRuby in certain circumstances, this
-
# is necessary here because we can't dup/clone UnboundMethods.
-
#
-
# This is necessary due to a bug in JRuby prior to 1.7.5 fixed in:
-
# https://github.com/jruby/jruby/commit/99a0613fe29935150d76a9a1ee4cf2b4f63f4a27
-
1
if RUBY_PLATFORM == 'java' && JRUBY_VERSION.split('.')[-1].to_i < 5
-
def find_method(mod)
-
mod.dup.instance_method(@method_name)
-
end
-
else
-
1
def find_method(mod)
-
mod.instance_method(@method_name)
-
end
-
end
-
-
1
def visibility_from(mod)
-
MethodReference.instance_method_visibility_for(mod, @method_name)
-
end
-
end
-
-
# @private
-
1
class ObjectMethodReference < MethodReference
-
1
def self.for(object_reference, method_name)
-
if ClassNewMethodReference.applies_to?(method_name) { object_reference.when_loaded { |o| o } }
-
ClassNewMethodReference.new(object_reference, method_name)
-
else
-
super
-
end
-
end
-
-
1
private
-
-
1
def method_implemented?(object)
-
object.respond_to?(@method_name, true)
-
end
-
-
1
def method_defined?(object)
-
(class << object; self; end).method_defined?(@method_name)
-
end
-
-
1
def find_method(object)
-
object.method(@method_name)
-
end
-
-
1
def visibility_from(object)
-
MethodReference.method_visibility_for(object, @method_name)
-
end
-
end
-
-
# When a class's `.new` method is stubbed, we want to use the method
-
# signature from `#initialize` because `.new`'s signature is a generic
-
# `def new(*args)` and it simply delegates to `#initialize` and forwards
-
# all args...so the method with the actually used signature is `#initialize`.
-
#
-
# This method reference implementation handles that specific case.
-
# @private
-
1
class ClassNewMethodReference < ObjectMethodReference
-
1
def self.applies_to?(method_name)
-
return false unless method_name == :new
-
klass = yield
-
return false unless klass.respond_to?(:new, true)
-
-
# We only want to apply our special logic to normal `new` methods.
-
# Methods that the user has monkeyed with should be left as-is.
-
klass.method(:new).owner == ::Class
-
end
-
-
1
def with_signature
-
@object_reference.when_loaded do |klass|
-
yield Support::MethodSignature.new(klass.instance_method(:initialize))
-
end
-
end
-
end
-
end
-
end
-
1
RSpec::Support.require_rspec_support 'recursive_const_methods'
-
-
1
module RSpec
-
1
module Mocks
-
# Provides information about constants that may (or may not)
-
# have been mutated by rspec-mocks.
-
1
class Constant
-
1
extend Support::RecursiveConstMethods
-
-
# @api private
-
1
def initialize(name)
-
@name = name
-
@previously_defined = false
-
@stubbed = false
-
@hidden = false
-
@valid_name = true
-
yield self if block_given?
-
end
-
-
# @return [String] The fully qualified name of the constant.
-
1
attr_reader :name
-
-
# @return [Object, nil] The original value (e.g. before it
-
# was mutated by rspec-mocks) of the constant, or
-
# nil if the constant was not previously defined.
-
1
attr_accessor :original_value
-
-
# @private
-
1
attr_writer :previously_defined, :stubbed, :hidden, :valid_name
-
-
# @return [Boolean] Whether or not the constant was defined
-
# before the current example.
-
1
def previously_defined?
-
@previously_defined
-
end
-
-
# @return [Boolean] Whether or not rspec-mocks has mutated
-
# (stubbed or hidden) this constant.
-
1
def mutated?
-
@stubbed || @hidden
-
end
-
-
# @return [Boolean] Whether or not rspec-mocks has stubbed
-
# this constant.
-
1
def stubbed?
-
@stubbed
-
end
-
-
# @return [Boolean] Whether or not rspec-mocks has hidden
-
# this constant.
-
1
def hidden?
-
@hidden
-
end
-
-
# @return [Boolean] Whether or not the provided constant name
-
# is a valid Ruby constant name.
-
1
def valid_name?
-
@valid_name
-
end
-
-
# The default `to_s` isn't very useful, so a custom version is provided.
-
1
def to_s
-
"#<#{self.class.name} #{name}>"
-
end
-
1
alias inspect to_s
-
-
# @private
-
1
def self.unmutated(name)
-
previously_defined = recursive_const_defined?(name)
-
rescue NameError
-
new(name) do |c|
-
c.valid_name = false
-
end
-
else
-
new(name) do |const|
-
const.previously_defined = previously_defined
-
const.original_value = recursive_const_get(name) if previously_defined
-
end
-
end
-
-
# Queries rspec-mocks to find out information about the named constant.
-
#
-
# @param [String] name the name of the constant
-
# @return [Constant] an object contaning information about the named
-
# constant.
-
1
def self.original(name)
-
mutator = ::RSpec::Mocks.space.constant_mutator_for(name)
-
mutator ? mutator.to_constant : unmutated(name)
-
end
-
end
-
-
# Provides a means to stub constants.
-
1
class ConstantMutator
-
1
extend Support::RecursiveConstMethods
-
-
# Stubs a constant.
-
#
-
# @param (see ExampleMethods#stub_const)
-
# @option (see ExampleMethods#stub_const)
-
# @return (see ExampleMethods#stub_const)
-
#
-
# @see ExampleMethods#stub_const
-
# @note It's recommended that you use `stub_const` in your
-
# examples. This is an alternate public API that is provided
-
# so you can stub constants in other contexts (e.g. helper
-
# classes).
-
1
def self.stub(constant_name, value, options={})
-
mutator = if recursive_const_defined?(constant_name, &raise_on_invalid_const)
-
DefinedConstantReplacer
-
else
-
UndefinedConstantSetter
-
end
-
-
mutate(mutator.new(constant_name, value, options[:transfer_nested_constants]))
-
value
-
end
-
-
# Hides a constant.
-
#
-
# @param (see ExampleMethods#hide_const)
-
#
-
# @see ExampleMethods#hide_const
-
# @note It's recommended that you use `hide_const` in your
-
# examples. This is an alternate public API that is provided
-
# so you can hide constants in other contexts (e.g. helper
-
# classes).
-
1
def self.hide(constant_name)
-
mutate(ConstantHider.new(constant_name, nil, {}))
-
nil
-
end
-
-
# Contains common functionality used by all of the constant mutators.
-
#
-
# @private
-
1
class BaseMutator
-
1
include Support::RecursiveConstMethods
-
-
1
attr_reader :original_value, :full_constant_name
-
-
1
def initialize(full_constant_name, mutated_value, transfer_nested_constants)
-
@full_constant_name = normalize_const_name(full_constant_name)
-
@mutated_value = mutated_value
-
@transfer_nested_constants = transfer_nested_constants
-
@context_parts = @full_constant_name.split('::')
-
@const_name = @context_parts.pop
-
@reset_performed = false
-
end
-
-
1
def to_constant
-
const = Constant.new(full_constant_name)
-
const.original_value = original_value
-
-
const
-
end
-
-
1
def idempotently_reset
-
reset unless @reset_performed
-
@reset_performed = true
-
end
-
end
-
-
# Hides a defined constant for the duration of an example.
-
#
-
# @private
-
1
class ConstantHider < BaseMutator
-
1
def mutate
-
return unless (@defined = recursive_const_defined?(full_constant_name))
-
@context = recursive_const_get(@context_parts.join('::'))
-
@original_value = get_const_defined_on(@context, @const_name)
-
-
@context.__send__(:remove_const, @const_name)
-
end
-
-
1
def to_constant
-
return Constant.unmutated(full_constant_name) unless @defined
-
-
const = super
-
const.hidden = true
-
const.previously_defined = true
-
-
const
-
end
-
-
1
def reset
-
return unless @defined
-
@context.const_set(@const_name, @original_value)
-
end
-
end
-
-
# Replaces a defined constant for the duration of an example.
-
#
-
# @private
-
1
class DefinedConstantReplacer < BaseMutator
-
1
def initialize(*args)
-
super
-
@constants_to_transfer = []
-
end
-
-
1
def mutate
-
@context = recursive_const_get(@context_parts.join('::'))
-
@original_value = get_const_defined_on(@context, @const_name)
-
-
@constants_to_transfer = verify_constants_to_transfer!
-
-
@context.__send__(:remove_const, @const_name)
-
@context.const_set(@const_name, @mutated_value)
-
-
transfer_nested_constants
-
end
-
-
1
def to_constant
-
const = super
-
const.stubbed = true
-
const.previously_defined = true
-
-
const
-
end
-
-
1
def reset
-
@constants_to_transfer.each do |const|
-
@mutated_value.__send__(:remove_const, const)
-
end
-
-
@context.__send__(:remove_const, @const_name)
-
@context.const_set(@const_name, @original_value)
-
end
-
-
1
def transfer_nested_constants
-
@constants_to_transfer.each do |const|
-
@mutated_value.const_set(const, get_const_defined_on(original_value, const))
-
end
-
end
-
-
1
def verify_constants_to_transfer!
-
return [] unless should_transfer_nested_constants?
-
-
{ @original_value => "the original value", @mutated_value => "the stubbed value" }.each do |value, description|
-
next if value.respond_to?(:constants)
-
-
raise ArgumentError,
-
"Cannot transfer nested constants for #{@full_constant_name} " \
-
"since #{description} is not a class or module and only classes " \
-
"and modules support nested constants."
-
end
-
-
if Array === @transfer_nested_constants
-
@transfer_nested_constants = @transfer_nested_constants.map(&:to_s) if RUBY_VERSION == '1.8.7'
-
undefined_constants = @transfer_nested_constants - constants_defined_on(@original_value)
-
-
if undefined_constants.any?
-
available_constants = constants_defined_on(@original_value) - @transfer_nested_constants
-
raise ArgumentError,
-
"Cannot transfer nested constant(s) #{undefined_constants.join(' and ')} " \
-
"for #{@full_constant_name} since they are not defined. Did you mean " \
-
"#{available_constants.join(' or ')}?"
-
end
-
-
@transfer_nested_constants
-
else
-
constants_defined_on(@original_value)
-
end
-
end
-
-
1
def should_transfer_nested_constants?
-
return true if @transfer_nested_constants
-
return false unless RSpec::Mocks.configuration.transfer_nested_constants?
-
@original_value.respond_to?(:constants) && @mutated_value.respond_to?(:constants)
-
end
-
end
-
-
# Sets an undefined constant for the duration of an example.
-
#
-
# @private
-
1
class UndefinedConstantSetter < BaseMutator
-
1
def mutate
-
@parent = @context_parts.inject(Object) do |klass, name|
-
if const_defined_on?(klass, name)
-
get_const_defined_on(klass, name)
-
else
-
ConstantMutator.stub(name_for(klass, name), Module.new)
-
end
-
end
-
-
@parent.const_set(@const_name, @mutated_value)
-
end
-
-
1
def to_constant
-
const = super
-
const.stubbed = true
-
const.previously_defined = false
-
-
const
-
end
-
-
1
def reset
-
@parent.__send__(:remove_const, @const_name)
-
end
-
-
1
private
-
-
1
def name_for(parent, name)
-
root = if parent == Object
-
''
-
else
-
parent.name
-
end
-
root + '::' + name
-
end
-
end
-
-
# Uses the mutator to mutate (stub or hide) a constant. Ensures that
-
# the mutator is correctly registered so it can be backed out at the end
-
# of the test.
-
#
-
# @private
-
1
def self.mutate(mutator)
-
::RSpec::Mocks.space.register_constant_mutator(mutator)
-
mutator.mutate
-
end
-
-
# Used internally by the constant stubbing to raise a helpful
-
# error when a constant like "A::B::C" is stubbed and A::B is
-
# not a module (and thus, it's impossible to define "A::B::C"
-
# since only modules can have nested constants).
-
#
-
# @api private
-
1
def self.raise_on_invalid_const
-
lambda do |const_name, failed_name|
-
raise "Cannot stub constant #{failed_name} on #{const_name} " \
-
"since #{const_name} is not a module."
-
end
-
end
-
end
-
end
-
end
-
1
module RSpec
-
1
module Mocks
-
# @private
-
1
class ObjectReference
-
# Returns an appropriate Object or Module reference based
-
# on the given argument.
-
1
def self.for(object_module_or_name, allow_direct_object_refs=false)
-
case object_module_or_name
-
when Module
-
if anonymous_module?(object_module_or_name)
-
DirectObjectReference.new(object_module_or_name)
-
else
-
# Use a `NamedObjectReference` if it has a name because this
-
# will use the original value of the constant in case it has
-
# been stubbed.
-
NamedObjectReference.new(name_of(object_module_or_name))
-
end
-
when String
-
NamedObjectReference.new(object_module_or_name)
-
else
-
if allow_direct_object_refs
-
DirectObjectReference.new(object_module_or_name)
-
else
-
raise ArgumentError,
-
"Module or String expected, got #{object_module_or_name.inspect}"
-
end
-
end
-
end
-
-
1
if Module.new.name.nil?
-
1
def self.anonymous_module?(mod)
-
!name_of(mod)
-
end
-
else # 1.8.7
-
def self.anonymous_module?(mod)
-
name_of(mod) == ""
-
end
-
end
-
1
private_class_method :anonymous_module?
-
-
1
def self.name_of(mod)
-
MODULE_NAME_METHOD.bind(mod).call
-
end
-
1
private_class_method :name_of
-
-
# @private
-
1
MODULE_NAME_METHOD = Module.instance_method(:name)
-
end
-
-
# An implementation of rspec-mocks' reference interface.
-
# Used when an object is passed to {ExampleMethods#object_double}, or
-
# an anonymous class or module is passed to {ExampleMethods#instance_double}
-
# or {ExampleMethods#class_double}.
-
# Represents a reference to that object.
-
# @see NamedObjectReference
-
1
class DirectObjectReference
-
# @param object [Object] the object to which this refers
-
1
def initialize(object)
-
@object = object
-
end
-
-
# @return [String] the object's description (via `#inspect`).
-
1
def description
-
@object.inspect
-
end
-
-
# Defined for interface parity with the other object reference
-
# implementations. Raises an `ArgumentError` to indicate that `as_stubbed_const`
-
# is invalid when passing an object argument to `object_double`.
-
1
def const_to_replace
-
raise ArgumentError,
-
"Can not perform constant replacement with an anonymous object."
-
end
-
-
# The target of the verifying double (the object itself).
-
#
-
# @return [Object]
-
1
def target
-
@object
-
end
-
-
# Always returns true for an object as the class is defined.
-
#
-
# @return [true]
-
1
def defined?
-
true
-
end
-
-
# Yields if the reference target is loaded, providing a generic mechanism
-
# to optionally run a bit of code only when a reference's target is
-
# loaded.
-
#
-
# This specific implementation always yields because direct references
-
# are always loaded.
-
#
-
# @yield [Object] the target of this reference.
-
1
def when_loaded
-
yield @object
-
end
-
end
-
-
# An implementation of rspec-mocks' reference interface.
-
# Used when a string is passed to {ExampleMethods#object_double},
-
# and when a string, named class or named module is passed to
-
# {ExampleMethods#instance_double}, or {ExampleMethods#class_double}.
-
# Represents a reference to the object named (via a constant lookup)
-
# by the string.
-
# @see DirectObjectReference
-
1
class NamedObjectReference
-
# @param const_name [String] constant name
-
1
def initialize(const_name)
-
@const_name = const_name
-
end
-
-
# @return [Boolean] true if the named constant is defined, false otherwise.
-
1
def defined?
-
!!object
-
end
-
-
# @return [String] the constant name to replace with a double.
-
1
def const_to_replace
-
@const_name
-
end
-
1
alias description const_to_replace
-
-
# @return [Object, nil] the target of the verifying double (the named object), or
-
# nil if it is not defined.
-
1
def target
-
object
-
end
-
-
# Yields if the reference target is loaded, providing a generic mechanism
-
# to optionally run a bit of code only when a reference's target is
-
# loaded.
-
#
-
# @yield [Object] the target object
-
1
def when_loaded
-
yield object if object
-
end
-
-
1
private
-
-
1
def object
-
return @object if defined?(@object)
-
@object = Constant.original(@const_name).original_value
-
end
-
end
-
end
-
end
-
1
module RSpec
-
1
module Mocks
-
# @private
-
1
class OrderGroup
-
1
def initialize
-
2
@expectations = []
-
2
@invocation_order = []
-
2
@index = 0
-
end
-
-
# @private
-
1
def register(expectation)
-
@expectations << expectation
-
end
-
-
1
def invoked(message)
-
@invocation_order << message
-
end
-
-
# @private
-
1
def ready_for?(expectation)
-
remaining_expectations.find(&:ordered?) == expectation
-
end
-
-
# @private
-
1
def consume
-
remaining_expectations.each_with_index do |expectation, index|
-
next unless expectation.ordered?
-
-
@index += index + 1
-
return expectation
-
end
-
nil
-
end
-
-
# @private
-
1
def handle_order_constraint(expectation)
-
return unless expectation.ordered? && remaining_expectations.include?(expectation)
-
return consume if ready_for?(expectation)
-
expectation.raise_out_of_order_error
-
end
-
-
1
def verify_invocation_order(expectation)
-
expectation.raise_out_of_order_error unless expectations_invoked_in_order?
-
true
-
end
-
-
1
def clear
-
@index = 0
-
@invocation_order.clear
-
@expectations.clear
-
end
-
-
1
def empty?
-
@expectations.empty?
-
end
-
-
1
private
-
-
1
def remaining_expectations
-
@expectations[@index..-1] || []
-
end
-
-
1
def expectations_invoked_in_order?
-
invoked_expectations == expected_invocations
-
end
-
-
1
def invoked_expectations
-
@expectations.select { |e| e.ordered? && @invocation_order.include?(e) }
-
end
-
-
1
def expected_invocations
-
@invocation_order.map { |invocation| expectation_for(invocation) }.compact
-
end
-
-
1
def expectation_for(message)
-
@expectations.find { |e| message == e }
-
end
-
end
-
end
-
end
-
1
module RSpec
-
1
module Mocks
-
# @private
-
1
class Proxy
-
1
SpecificMessage = Struct.new(:object, :message, :args) do
-
1
def ==(expectation)
-
expectation.orig_object == object && expectation.matches?(message, *args)
-
end
-
end
-
-
# @private
-
1
def ensure_implemented(*_args)
-
# noop for basic proxies, see VerifyingProxy for behaviour.
-
end
-
-
# @private
-
1
def initialize(object, order_group, options={})
-
@object = object
-
@order_group = order_group
-
@error_generator = ErrorGenerator.new(object)
-
@messages_received = []
-
@options = options
-
@null_object = false
-
@method_doubles = Hash.new { |h, k| h[k] = MethodDouble.new(@object, k, self) }
-
end
-
-
# @private
-
1
attr_reader :object
-
-
# @private
-
1
def null_object?
-
@null_object
-
end
-
-
# @private
-
# Tells the object to ignore any messages that aren't explicitly set as
-
# stubs or message expectations.
-
1
def as_null_object
-
@null_object = true
-
@object
-
end
-
-
# @private
-
1
def original_method_handle_for(_message)
-
nil
-
end
-
-
1
DEFAULT_MESSAGE_EXPECTATION_OPTS = {}.freeze
-
-
# @private
-
1
def add_message_expectation(method_name, opts=DEFAULT_MESSAGE_EXPECTATION_OPTS, &block)
-
location = opts.fetch(:expected_from) { CallerFilter.first_non_rspec_line }
-
meth_double = method_double_for(method_name)
-
-
if null_object? && !block
-
meth_double.add_default_stub(@error_generator, @order_group, location, opts) do
-
@object
-
end
-
end
-
-
meth_double.add_expectation @error_generator, @order_group, location, opts, &block
-
end
-
-
# @private
-
1
def add_simple_expectation(method_name, response, location)
-
method_double_for(method_name).add_simple_expectation method_name, response, @error_generator, location
-
end
-
-
# @private
-
1
def build_expectation(method_name)
-
meth_double = method_double_for(method_name)
-
-
meth_double.build_expectation(
-
@error_generator,
-
@order_group
-
)
-
end
-
-
# @private
-
1
def replay_received_message_on(expectation, &block)
-
expected_method_name = expectation.message
-
meth_double = method_double_for(expected_method_name)
-
-
if meth_double.expectations.any?
-
@error_generator.raise_expectation_on_mocked_method(expected_method_name)
-
end
-
-
unless null_object? || meth_double.stubs.any?
-
@error_generator.raise_expectation_on_unstubbed_method(expected_method_name)
-
end
-
-
@messages_received.each do |(actual_method_name, args, _)|
-
next unless expectation.matches?(actual_method_name, *args)
-
-
expectation.safe_invoke(nil)
-
block.call(*args) if block
-
end
-
end
-
-
# @private
-
1
def check_for_unexpected_arguments(expectation)
-
return if @messages_received.empty?
-
-
return if @messages_received.any? { |method_name, args, _| expectation.matches?(method_name, *args) }
-
-
name_but_not_args, others = @messages_received.partition do |(method_name, args, _)|
-
expectation.matches_name_but_not_args(method_name, *args)
-
end
-
-
return if name_but_not_args.empty? && !others.empty?
-
-
expectation.raise_unexpected_message_args_error(name_but_not_args.map { |args| args[1] })
-
end
-
-
# @private
-
1
def add_stub(method_name, opts={}, &implementation)
-
location = opts.fetch(:expected_from) { CallerFilter.first_non_rspec_line }
-
method_double_for(method_name).add_stub @error_generator, @order_group, location, opts, &implementation
-
end
-
-
# @private
-
1
def add_simple_stub(method_name, response)
-
method_double_for(method_name).add_simple_stub method_name, response
-
end
-
-
# @private
-
1
def remove_stub(method_name)
-
method_double_for(method_name).remove_stub
-
end
-
-
# @private
-
1
def remove_stub_if_present(method_name)
-
method_double_for(method_name).remove_stub_if_present
-
end
-
-
# @private
-
1
def verify
-
@method_doubles.each_value { |d| d.verify }
-
end
-
-
# @private
-
1
def reset
-
@messages_received.clear
-
end
-
-
# @private
-
1
def received_message?(method_name, *args, &block)
-
@messages_received.any? { |array| array == [method_name, args, block] }
-
end
-
-
# @private
-
1
def messages_arg_list
-
@messages_received.map { |_, args, _| args }
-
end
-
-
# @private
-
1
def has_negative_expectation?(message)
-
method_double_for(message).expectations.find { |expectation| expectation.negative_expectation_for?(message) }
-
end
-
-
# @private
-
1
def record_message_received(message, *args, &block)
-
@order_group.invoked SpecificMessage.new(object, message, args)
-
@messages_received << [message, args, block]
-
end
-
-
# @private
-
1
def message_received(message, *args, &block)
-
record_message_received message, *args, &block
-
-
expectation = find_matching_expectation(message, *args)
-
stub = find_matching_method_stub(message, *args)
-
-
if (stub && expectation && expectation.called_max_times?) || (stub && !expectation)
-
expectation.increase_actual_received_count! if expectation && expectation.actual_received_count_matters?
-
if (expectation = find_almost_matching_expectation(message, *args))
-
expectation.advise(*args) unless expectation.expected_messages_received?
-
end
-
stub.invoke(nil, *args, &block)
-
elsif expectation
-
expectation.unadvise(messages_arg_list)
-
expectation.invoke(stub, *args, &block)
-
elsif (expectation = find_almost_matching_expectation(message, *args))
-
expectation.advise(*args) if null_object? unless expectation.expected_messages_received?
-
-
if null_object? || !has_negative_expectation?(message)
-
expectation.raise_unexpected_message_args_error([args])
-
end
-
elsif (stub = find_almost_matching_stub(message, *args))
-
stub.advise(*args)
-
raise_missing_default_stub_error(stub, [args])
-
elsif Class === @object
-
@object.superclass.__send__(message, *args, &block)
-
else
-
@object.__send__(:method_missing, message, *args, &block)
-
end
-
end
-
-
# @private
-
1
def raise_unexpected_message_error(method_name, args)
-
@error_generator.raise_unexpected_message_error method_name, args
-
end
-
-
# @private
-
1
def raise_missing_default_stub_error(expectation, args_for_multiple_calls)
-
@error_generator.raise_missing_default_stub_error(expectation, args_for_multiple_calls)
-
end
-
-
# @private
-
1
def visibility_for(_method_name)
-
# This is the default (for test doubles). Subclasses override this.
-
:public
-
end
-
-
1
if Support::RubyFeatures.module_prepends_supported?
-
1
def self.prepended_modules_of(klass)
-
ancestors = klass.ancestors
-
-
# `|| 0` is necessary for Ruby 2.0, where the singleton class
-
# is only in the ancestor list when there are prepended modules.
-
singleton_index = ancestors.index(klass) || 0
-
-
ancestors[0, singleton_index]
-
end
-
-
1
def prepended_modules_of_singleton_class
-
@prepended_modules_of_singleton_class ||= RSpec::Mocks::Proxy.prepended_modules_of(@object.singleton_class)
-
end
-
end
-
-
1
private
-
-
1
def method_double_for(message)
-
@method_doubles[message.to_sym]
-
end
-
-
1
def find_matching_expectation(method_name, *args)
-
find_best_matching_expectation_for(method_name) do |expectation|
-
expectation.matches?(method_name, *args)
-
end
-
end
-
-
1
def find_almost_matching_expectation(method_name, *args)
-
find_best_matching_expectation_for(method_name) do |expectation|
-
expectation.matches_name_but_not_args(method_name, *args)
-
end
-
end
-
-
1
def find_best_matching_expectation_for(method_name)
-
first_match = nil
-
-
method_double_for(method_name).expectations.each do |expectation|
-
next unless yield expectation
-
return expectation unless expectation.called_max_times?
-
first_match ||= expectation
-
end
-
-
first_match
-
end
-
-
1
def find_matching_method_stub(method_name, *args)
-
method_double_for(method_name).stubs.find { |stub| stub.matches?(method_name, *args) }
-
end
-
-
1
def find_almost_matching_stub(method_name, *args)
-
method_double_for(method_name).stubs.find { |stub| stub.matches_name_but_not_args(method_name, *args) }
-
end
-
end
-
-
# @private
-
1
class TestDoubleProxy < Proxy
-
1
def reset
-
@method_doubles.clear
-
object.__disallow_further_usage!
-
super
-
end
-
end
-
-
# @private
-
1
class PartialDoubleProxy < Proxy
-
1
def original_method_handle_for(message)
-
if any_instance_class_recorder_observing_method?(@object.class, message)
-
message = ::RSpec::Mocks.space.
-
any_instance_recorder_for(@object.class).
-
build_alias_method_name(message)
-
end
-
-
::RSpec::Support.method_handle_for(@object, message)
-
rescue NameError
-
nil
-
end
-
-
# @private
-
1
def add_simple_expectation(method_name, response, location)
-
method_double_for(method_name).configure_method
-
super
-
end
-
-
# @private
-
1
def add_simple_stub(method_name, response)
-
method_double_for(method_name).configure_method
-
super
-
end
-
-
# @private
-
1
def visibility_for(method_name)
-
# We fall back to :public because by default we allow undefined methods
-
# to be stubbed, and when we do so, we make them public.
-
MethodReference.method_visibility_for(@object, method_name) || :public
-
end
-
-
1
def reset
-
@method_doubles.each_value { |d| d.reset }
-
super
-
end
-
-
1
def message_received(message, *args, &block)
-
RSpec::Mocks.space.any_instance_recorders_from_ancestry_of(object).each do |subscriber|
-
subscriber.notify_received_message(object, message, args, block)
-
end
-
super
-
end
-
-
1
private
-
-
1
def any_instance_class_recorder_observing_method?(klass, method_name)
-
only_return_existing = true
-
recorder = ::RSpec::Mocks.space.any_instance_recorder_for(klass, only_return_existing)
-
return true if recorder && recorder.already_observing?(method_name)
-
-
superklass = klass.superclass
-
return false if superklass.nil?
-
any_instance_class_recorder_observing_method?(superklass, method_name)
-
end
-
end
-
-
# @private
-
# When we mock or stub a method on a class, we have to treat it a bit different,
-
# because normally singleton method definitions only affect the object on which
-
# they are defined, but on classes they affect subclasses, too. As a result,
-
# we need some special handling to get the original method.
-
1
module PartialClassDoubleProxyMethods
-
1
def initialize(source_space, *args)
-
@source_space = source_space
-
super(*args)
-
end
-
-
# Consider this situation:
-
#
-
# class A; end
-
# class B < A; end
-
#
-
# allow(A).to receive(:new)
-
# expect(B).to receive(:new).and_call_original
-
#
-
# When getting the original definition for `B.new`, we cannot rely purely on
-
# using `B.method(:new)` before our redefinition is defined on `B`, because
-
# `B.method(:new)` will return a method that will execute the stubbed version
-
# of the method on `A` since singleton methods on classes are in the lookup
-
# hierarchy.
-
#
-
# To do it properly, we need to find the original definition of `new` from `A`
-
# from _before_ `A` was stubbed, and we need to rebind it to `B` so that it will
-
# run with the proper `self`.
-
#
-
# That's what this method (together with `original_unbound_method_handle_from_ancestor_for`)
-
# does.
-
1
def original_method_handle_for(message)
-
unbound_method = superclass_proxy &&
-
superclass_proxy.original_unbound_method_handle_from_ancestor_for(message.to_sym)
-
-
return super unless unbound_method
-
unbound_method.bind(object)
-
end
-
-
1
protected
-
-
1
def original_unbound_method_handle_from_ancestor_for(message)
-
method_double = @method_doubles.fetch(message) do
-
# The fact that there is no method double for this message indicates
-
# that it has not been redefined by rspec-mocks. We need to continue
-
# looking up the ancestor chain.
-
return superclass_proxy &&
-
superclass_proxy.original_unbound_method_handle_from_ancestor_for(message)
-
end
-
-
method_double.original_method.unbind
-
end
-
-
1
def superclass_proxy
-
return @superclass_proxy if defined?(@superclass_proxy)
-
-
if (superclass = object.superclass)
-
@superclass_proxy = @source_space.superclass_proxy_for(superclass)
-
else
-
@superclass_proxy = nil
-
end
-
end
-
end
-
-
# @private
-
1
class PartialClassDoubleProxy < PartialDoubleProxy
-
1
include PartialClassDoubleProxyMethods
-
end
-
-
# @private
-
1
class ProxyForNil < PartialDoubleProxy
-
1
def initialize(order_group)
-
@warn_about_expectations = true
-
super(nil, order_group)
-
end
-
-
1
attr_accessor :warn_about_expectations
-
1
alias warn_about_expectations? warn_about_expectations
-
-
1
def add_message_expectation(method_name, opts={}, &block)
-
warn(method_name) if warn_about_expectations?
-
super
-
end
-
-
1
def add_negative_message_expectation(location, method_name, &implementation)
-
warn(method_name) if warn_about_expectations?
-
super
-
end
-
-
1
def add_stub(method_name, opts={}, &implementation)
-
warn(method_name) if warn_about_expectations?
-
super
-
end
-
-
1
private
-
-
1
def warn(method_name)
-
source = CallerFilter.first_non_rspec_line
-
Kernel.warn("An expectation of :#{method_name} was set on nil. Called from #{source}. Use allow_message_expectations_on_nil to disable warnings.")
-
end
-
end
-
end
-
end
-
1
module RSpec
-
1
module Mocks
-
# Allows a thread to lock out other threads from a critical section of code,
-
# while allowing the thread with the lock to reenter that section.
-
#
-
# Based on Monitor as of 2.2 -
-
# https://github.com/ruby/ruby/blob/eb7ddaa3a47bf48045d26c72eb0f263a53524ebc/lib/monitor.rb#L9
-
#
-
# Depends on Mutex, but Mutex is only available as part of core since 1.9.1:
-
# exists - http://ruby-doc.org/core-1.9.1/Mutex.html
-
# dne - http://ruby-doc.org/core-1.9.0/Mutex.html
-
#
-
# @private
-
1
class ReentrantMutex
-
1
def initialize
-
4
@owner = nil
-
4
@count = 0
-
4
@mutex = Mutex.new
-
end
-
-
1
def synchronize
-
enter
-
yield
-
ensure
-
exit
-
end
-
-
1
private
-
-
1
def enter
-
@mutex.lock if @owner != Thread.current
-
@owner = Thread.current
-
@count += 1
-
end
-
-
1
def exit
-
@count -= 1
-
return unless @count == 0
-
@owner = nil
-
@mutex.unlock
-
end
-
end
-
-
1
if defined? ::Mutex
-
# On 1.9 and up, this is in core, so we just use the real one
-
1
Mutex = ::Mutex
-
else # For 1.8.7
-
# :nocov:
-
skipped
RSpec::Support.require_rspec_mocks "mutex"
-
# :nocov:
-
end
-
end
-
end
-
1
RSpec::Support.require_rspec_mocks 'reentrant_mutex'
-
-
1
module RSpec
-
1
module Mocks
-
# @private
-
# Provides a default space implementation for outside
-
# the scope of an example. Called "root" because it serves
-
# as the root of the space stack.
-
1
class RootSpace
-
1
def proxy_for(*_args)
-
raise_lifecycle_message
-
end
-
-
1
def any_instance_recorder_for(*_args)
-
raise_lifecycle_message
-
end
-
-
1
def any_instance_proxy_for(*_args)
-
raise_lifecycle_message
-
end
-
-
1
def register_constant_mutator(_mutator)
-
raise_lifecycle_message
-
end
-
-
1
def any_instance_recorders_from_ancestry_of(_object)
-
raise_lifecycle_message
-
end
-
-
1
def reset_all
-
end
-
-
1
def verify_all
-
end
-
-
1
def registered?(_object)
-
false
-
end
-
-
1
def superclass_proxy_for(*_args)
-
raise_lifecycle_message
-
end
-
-
1
def new_scope
-
2
Space.new
-
end
-
-
1
private
-
-
1
def raise_lifecycle_message
-
raise OutsideOfExampleError,
-
"The use of doubles or partial doubles from rspec-mocks outside of the per-test lifecycle is not supported."
-
end
-
end
-
-
# @private
-
1
class Space
-
1
attr_reader :proxies, :any_instance_recorders, :proxy_mutex, :any_instance_mutex
-
-
1
def initialize
-
2
@proxies = {}
-
2
@any_instance_recorders = {}
-
2
@constant_mutators = []
-
2
@expectation_ordering = OrderGroup.new
-
2
@proxy_mutex = new_mutex
-
2
@any_instance_mutex = new_mutex
-
end
-
-
1
def new_scope
-
NestedSpace.new(self)
-
end
-
-
1
def verify_all
-
1
proxies.values.each { |proxy| proxy.verify }
-
1
any_instance_recorders.each_value { |recorder| recorder.verify }
-
end
-
-
1
def reset_all
-
2
proxies.each_value { |proxy| proxy.reset }
-
2
@constant_mutators.reverse.each { |mut| mut.idempotently_reset }
-
2
any_instance_recorders.each_value { |recorder| recorder.stop_all_observation! }
-
2
any_instance_recorders.clear
-
end
-
-
1
def register_constant_mutator(mutator)
-
@constant_mutators << mutator
-
end
-
-
1
def constant_mutator_for(name)
-
@constant_mutators.find { |m| m.full_constant_name == name }
-
end
-
-
1
def any_instance_recorder_for(klass, only_return_existing=false)
-
any_instance_mutex.synchronize do
-
id = klass.__id__
-
any_instance_recorders.fetch(id) do
-
return nil if only_return_existing
-
any_instance_recorder_not_found_for(id, klass)
-
end
-
end
-
end
-
-
1
def any_instance_proxy_for(klass)
-
AnyInstance::Proxy.new(any_instance_recorder_for(klass), proxies_of(klass))
-
end
-
-
1
def proxies_of(klass)
-
proxies.values.select { |proxy| klass === proxy.object }
-
end
-
-
1
def proxy_for(object)
-
proxy_mutex.synchronize do
-
id = id_for(object)
-
proxies.fetch(id) { proxy_not_found_for(id, object) }
-
end
-
end
-
-
1
def superclass_proxy_for(klass)
-
proxy_mutex.synchronize do
-
id = id_for(klass)
-
proxies.fetch(id) { superclass_proxy_not_found_for(id, klass) }
-
end
-
end
-
-
1
alias ensure_registered proxy_for
-
-
1
def registered?(object)
-
proxies.key?(id_for object)
-
end
-
-
1
def any_instance_recorders_from_ancestry_of(object)
-
# Optimization: `any_instance` is a feature we generally
-
# recommend not using, so we can often early exit here
-
# without doing an O(N) linear search over the number of
-
# ancestors in the object's class hierarchy.
-
return [] if any_instance_recorders.empty?
-
-
# We access the ancestors through the singleton class, to avoid calling
-
# `class` in case `class` has been stubbed.
-
(class << object; ancestors; end).map do |klass|
-
any_instance_recorders[klass.__id__]
-
end.compact
-
end
-
-
1
private
-
-
1
def new_mutex
-
4
Mocks::ReentrantMutex.new
-
end
-
-
1
def proxy_not_found_for(id, object)
-
proxies[id] = case object
-
when NilClass then ProxyForNil.new(@expectation_ordering)
-
when TestDouble then object.__build_mock_proxy_unless_expired(@expectation_ordering)
-
when Class
-
class_proxy_with_callback_verification_strategy(object, CallbackInvocationStrategy.new)
-
else
-
if RSpec::Mocks.configuration.verify_partial_doubles?
-
VerifyingPartialDoubleProxy.new(object, @expectation_ordering)
-
else
-
PartialDoubleProxy.new(object, @expectation_ordering)
-
end
-
end
-
end
-
-
1
def superclass_proxy_not_found_for(id, object)
-
raise "superclass_proxy_not_found_for called with something that is not a class" unless Class === object
-
proxies[id] = class_proxy_with_callback_verification_strategy(object, NoCallbackInvocationStrategy.new)
-
end
-
-
1
def class_proxy_with_callback_verification_strategy(object, strategy)
-
if RSpec::Mocks.configuration.verify_partial_doubles?
-
VerifyingPartialClassDoubleProxy.new(
-
self,
-
object,
-
@expectation_ordering,
-
strategy
-
)
-
else
-
PartialClassDoubleProxy.new(self, object, @expectation_ordering)
-
end
-
end
-
-
1
def any_instance_recorder_not_found_for(id, klass)
-
any_instance_recorders[id] = AnyInstance::Recorder.new(klass)
-
end
-
-
1
if defined?(::BasicObject) && !::BasicObject.method_defined?(:__id__) # for 1.9.2
-
require 'securerandom'
-
-
def id_for(object)
-
id = object.__id__
-
-
return id if object.equal?(::ObjectSpace._id2ref(id))
-
# this suggests that object.__id__ is proxying through to some wrapped object
-
-
object.instance_exec do
-
@__id_for_rspec_mocks_space ||= ::SecureRandom.uuid
-
end
-
end
-
else
-
1
def id_for(object)
-
object.__id__
-
end
-
end
-
end
-
-
# @private
-
1
class NestedSpace < Space
-
1
def initialize(parent)
-
@parent = parent
-
super()
-
end
-
-
1
def proxies_of(klass)
-
super + @parent.proxies_of(klass)
-
end
-
-
1
def constant_mutator_for(name)
-
super || @parent.constant_mutator_for(name)
-
end
-
-
1
def registered?(object)
-
super || @parent.registered?(object)
-
end
-
-
1
private
-
-
1
def proxy_not_found_for(id, object)
-
@parent.proxies[id] || super
-
end
-
-
1
def any_instance_recorder_not_found_for(id, klass)
-
@parent.any_instance_recorders[id] || super
-
end
-
end
-
end
-
end
-
1
module RSpec
-
1
module Mocks
-
# @api private
-
# Provides methods for enabling and disabling the available syntaxes
-
# provided by rspec-mocks.
-
1
module Syntax
-
# @private
-
1
def self.warn_about_should!
-
1
@warn_about_should = true
-
end
-
-
# @private
-
1
def self.warn_unless_should_configured(method_name , replacement="the new `:expect` syntax or explicitly enable `:should`")
-
if @warn_about_should
-
RSpec.deprecate(
-
"Using `#{method_name}` from rspec-mocks' old `:should` syntax without explicitly enabling the syntax",
-
:replacement => replacement
-
)
-
-
@warn_about_should = false
-
end
-
end
-
-
# @api private
-
# Enables the should syntax (`dbl.stub`, `dbl.should_receive`, etc).
-
1
def self.enable_should(syntax_host=default_should_syntax_host)
-
1
@warn_about_should = false if syntax_host == default_should_syntax_host
-
1
return if should_enabled?(syntax_host)
-
-
1
syntax_host.class_exec do
-
1
def should_receive(message, opts={}, &block)
-
::RSpec::Mocks::Syntax.warn_unless_should_configured(__method__)
-
::RSpec::Mocks.expect_message(self, message, opts, &block)
-
end
-
-
1
def should_not_receive(message, &block)
-
::RSpec::Mocks::Syntax.warn_unless_should_configured(__method__)
-
::RSpec::Mocks.expect_message(self, message, {}, &block).never
-
end
-
-
1
def stub(message_or_hash, opts={}, &block)
-
::RSpec::Mocks::Syntax.warn_unless_should_configured(__method__)
-
if ::Hash === message_or_hash
-
message_or_hash.each { |message, value| stub(message).and_return value }
-
else
-
::RSpec::Mocks.allow_message(self, message_or_hash, opts, &block)
-
end
-
end
-
-
1
def unstub(message)
-
::RSpec::Mocks::Syntax.warn_unless_should_configured(__method__, "`allow(...).to_receive(...).and_call_original` or explicitly enable `:should`")
-
::RSpec::Mocks.space.proxy_for(self).remove_stub(message)
-
end
-
-
1
def stub_chain(*chain, &blk)
-
::RSpec::Mocks::Syntax.warn_unless_should_configured(__method__)
-
::RSpec::Mocks::StubChain.stub_chain_on(self, *chain, &blk)
-
end
-
-
1
def as_null_object
-
::RSpec::Mocks::Syntax.warn_unless_should_configured(__method__)
-
@_null_object = true
-
::RSpec::Mocks.space.proxy_for(self).as_null_object
-
end
-
-
1
def null_object?
-
::RSpec::Mocks::Syntax.warn_unless_should_configured(__method__)
-
defined?(@_null_object)
-
end
-
-
1
def received_message?(message, *args, &block)
-
::RSpec::Mocks::Syntax.warn_unless_should_configured(__method__)
-
::RSpec::Mocks.space.proxy_for(self).received_message?(message, *args, &block)
-
end
-
-
1
unless Class.respond_to? :any_instance
-
1
Class.class_exec do
-
1
def any_instance
-
::RSpec::Mocks::Syntax.warn_unless_should_configured(__method__)
-
::RSpec::Mocks.space.any_instance_proxy_for(self)
-
end
-
end
-
end
-
end
-
end
-
-
# @api private
-
# Disables the should syntax (`dbl.stub`, `dbl.should_receive`, etc).
-
1
def self.disable_should(syntax_host=default_should_syntax_host)
-
return unless should_enabled?(syntax_host)
-
-
syntax_host.class_exec do
-
undef should_receive
-
undef should_not_receive
-
undef stub
-
undef unstub
-
undef stub_chain
-
undef as_null_object
-
undef null_object?
-
undef received_message?
-
end
-
-
Class.class_exec do
-
undef any_instance
-
end
-
end
-
-
# @api private
-
# Enables the expect syntax (`expect(dbl).to receive`, `allow(dbl).to receive`, etc).
-
1
def self.enable_expect(syntax_host=::RSpec::Mocks::ExampleMethods)
-
1
return if expect_enabled?(syntax_host)
-
-
1
syntax_host.class_exec do
-
1
def receive(method_name, &block)
-
Matchers::Receive.new(method_name, block)
-
end
-
-
1
def receive_messages(message_return_value_hash)
-
matcher = Matchers::ReceiveMessages.new(message_return_value_hash)
-
matcher.warn_about_block if block_given?
-
matcher
-
end
-
-
1
def receive_message_chain(*messages, &block)
-
Matchers::ReceiveMessageChain.new(messages, &block)
-
end
-
-
1
def allow(target)
-
AllowanceTarget.new(target)
-
end
-
-
1
def expect_any_instance_of(klass)
-
AnyInstanceExpectationTarget.new(klass)
-
end
-
-
1
def allow_any_instance_of(klass)
-
AnyInstanceAllowanceTarget.new(klass)
-
end
-
end
-
-
1
RSpec::Mocks::ExampleMethods::ExpectHost.class_exec do
-
1
def expect(target)
-
ExpectationTarget.new(target)
-
end
-
end
-
end
-
-
# @api private
-
# Disables the expect syntax (`expect(dbl).to receive`, `allow(dbl).to receive`, etc).
-
1
def self.disable_expect(syntax_host=::RSpec::Mocks::ExampleMethods)
-
return unless expect_enabled?(syntax_host)
-
-
syntax_host.class_exec do
-
undef receive
-
undef receive_messages
-
undef receive_message_chain
-
undef allow
-
undef expect_any_instance_of
-
undef allow_any_instance_of
-
end
-
-
RSpec::Mocks::ExampleMethods::ExpectHost.class_exec do
-
undef expect
-
end
-
end
-
-
# @api private
-
# Indicates whether or not the should syntax is enabled.
-
1
def self.should_enabled?(syntax_host=default_should_syntax_host)
-
1
syntax_host.method_defined?(:should_receive)
-
end
-
-
# @api private
-
# Indicates whether or not the expect syntax is enabled.
-
1
def self.expect_enabled?(syntax_host=::RSpec::Mocks::ExampleMethods)
-
1
syntax_host.method_defined?(:allow)
-
end
-
-
# @api private
-
# Determines where the methods like `should_receive`, and `stub` are added.
-
1
def self.default_should_syntax_host
-
# JRuby 1.7.4 introduces a regression whereby `defined?(::BasicObject) => nil`
-
# yet `BasicObject` still exists and patching onto ::Object breaks things
-
# e.g. SimpleDelegator expectations won't work
-
#
-
# See: https://github.com/jruby/jruby/issues/814
-
2
if defined?(JRUBY_VERSION) && JRUBY_VERSION == '1.7.4' && RUBY_VERSION.to_f > 1.8
-
return ::BasicObject
-
end
-
-
# On 1.8.7, Object.ancestors.last == Kernel but
-
# things blow up if we include `RSpec::Mocks::Methods`
-
# into Kernel...not sure why.
-
2
return Object unless defined?(::BasicObject)
-
-
# MacRuby has BasicObject but it's not the root class.
-
2
return Object unless Object.ancestors.last == ::BasicObject
-
-
2
::BasicObject
-
end
-
end
-
end
-
end
-
-
1
if defined?(BasicObject)
-
# The legacy `:should` syntax adds the following methods directly to
-
# `BasicObject` so that they are available off of any object. Note, however,
-
# that this syntax does not always play nice with delegate/proxy objects.
-
# We recommend you use the non-monkeypatching `:expect` syntax instead.
-
# @see Class
-
1
class BasicObject
-
# @method should_receive
-
# Sets an expectation that this object should receive a message before
-
# the end of the example.
-
#
-
# @example
-
# logger = double('logger')
-
# thing_that_logs = ThingThatLogs.new(logger)
-
# logger.should_receive(:log)
-
# thing_that_logs.do_something_that_logs_a_message
-
#
-
# @note This is only available when you have enabled the `should` syntax.
-
# @see RSpec::Mocks::ExampleMethods#expect
-
-
# @method should_not_receive
-
# Sets and expectation that this object should _not_ receive a message
-
# during this example.
-
# @see RSpec::Mocks::ExampleMethods#expect
-
-
# @method stub
-
# Tells the object to respond to the message with the specified value.
-
#
-
# @example
-
# counter.stub(:count).and_return(37)
-
# counter.stub(:count => 37)
-
# counter.stub(:count) { 37 }
-
#
-
# @note This is only available when you have enabled the `should` syntax.
-
# @see RSpec::Mocks::ExampleMethods#allow
-
-
# @method unstub
-
# Removes a stub. On a double, the object will no longer respond to
-
# `message`. On a real object, the original method (if it exists) is
-
# restored.
-
#
-
# This is rarely used, but can be useful when a stub is set up during a
-
# shared `before` hook for the common case, but you want to replace it
-
# for a special case.
-
#
-
# @note This is only available when you have enabled the `should` syntax.
-
-
# @method stub_chain
-
# @overload stub_chain(method1, method2)
-
# @overload stub_chain("method1.method2")
-
# @overload stub_chain(method1, method_to_value_hash)
-
#
-
# Stubs a chain of methods.
-
#
-
# ## Warning:
-
#
-
# Chains can be arbitrarily long, which makes it quite painless to
-
# violate the Law of Demeter in violent ways, so you should consider any
-
# use of `stub_chain` a code smell. Even though not all code smells
-
# indicate real problems (think fluent interfaces), `stub_chain` still
-
# results in brittle examples. For example, if you write
-
# `foo.stub_chain(:bar, :baz => 37)` in a spec and then the
-
# implementation calls `foo.baz.bar`, the stub will not work.
-
#
-
# @example
-
# double.stub_chain("foo.bar") { :baz }
-
# double.stub_chain(:foo, :bar => :baz)
-
# double.stub_chain(:foo, :bar) { :baz }
-
#
-
# # Given any of ^^ these three forms ^^:
-
# double.foo.bar # => :baz
-
#
-
# # Common use in Rails/ActiveRecord:
-
# Article.stub_chain("recent.published") { [Article.new] }
-
#
-
# @note This is only available when you have enabled the `should` syntax.
-
# @see RSpec::Mocks::ExampleMethods#receive_message_chain
-
-
# @method as_null_object
-
# Tells the object to respond to all messages. If specific stub values
-
# are declared, they'll work as expected. If not, the receiver is
-
# returned.
-
#
-
# @note This is only available when you have enabled the `should` syntax.
-
-
# @method null_object?
-
# Returns true if this object has received `as_null_object`
-
#
-
# @note This is only available when you have enabled the `should` syntax.
-
end
-
end
-
-
# The legacy `:should` syntax adds the `any_instance` to `Class`.
-
# We generally recommend you use the newer `:expect` syntax instead,
-
# which allows you to stub any instance of a class using
-
# `allow_any_instance_of(klass)` or mock any instance using
-
# `expect_any_instance_of(klass)`.
-
# @see BasicObject
-
1
class Class
-
# @method any_instance
-
# Used to set stubs and message expectations on any instance of a given
-
# class. Returns a [Recorder](Recorder), which records messages like
-
# `stub` and `should_receive` for later playback on instances of the
-
# class.
-
#
-
# @example
-
# Car.any_instance.should_receive(:go)
-
# race = Race.new
-
# race.cars << Car.new
-
# race.go # assuming this delegates to all of its cars
-
# # this example would pass
-
#
-
# Account.any_instance.stub(:balance) { Money.new(:USD, 25) }
-
# Account.new.balance # => Money.new(:USD, 25))
-
#
-
# @return [Recorder]
-
#
-
# @note This is only available when you have enabled the `should` syntax.
-
# @see RSpec::Mocks::ExampleMethods#expect_any_instance_of
-
# @see RSpec::Mocks::ExampleMethods#allow_any_instance_of
-
end
-
1
module RSpec
-
1
module Mocks
-
# @private
-
1
class TargetBase
-
1
def initialize(target)
-
@target = target
-
end
-
-
1
def self.delegate_to(matcher_method)
-
4
define_method(:to) do |matcher, &block|
-
unless matcher_allowed?(matcher)
-
raise_unsupported_matcher(:to, matcher)
-
end
-
define_matcher(matcher, matcher_method, &block)
-
end
-
end
-
-
1
def self.delegate_not_to(matcher_method, options={})
-
4
method_name = options.fetch(:from)
-
4
define_method(method_name) do |matcher, &block|
-
case matcher
-
when Matchers::Receive
-
define_matcher(matcher, matcher_method, &block)
-
when Matchers::ReceiveMessages, Matchers::ReceiveMessageChain
-
raise_negation_unsupported(method_name, matcher)
-
else
-
raise_unsupported_matcher(method_name, matcher)
-
end
-
end
-
end
-
-
1
def self.disallow_negation(method_name)
-
4
define_method(method_name) do |matcher, *_args|
-
raise_negation_unsupported(method_name, matcher)
-
end
-
end
-
-
1
private
-
-
1
def matcher_allowed?(matcher)
-
matcher.class.name.start_with?("RSpec::Mocks::Matchers".freeze)
-
end
-
-
1
def define_matcher(matcher, name, &block)
-
matcher.__send__(name, @target, &block)
-
end
-
-
1
def raise_unsupported_matcher(method_name, matcher)
-
raise UnsupportedMatcherError,
-
"only the `receive` or `receive_messages` matchers are supported " \
-
"with `#{expression}(...).#{method_name}`, but you have provided: #{matcher}"
-
end
-
-
1
def raise_negation_unsupported(method_name, matcher)
-
raise NegationUnsupportedError,
-
"`#{expression}(...).#{method_name} #{matcher.name}` is not supported since it " \
-
"doesn't really make sense. What would it even mean?"
-
end
-
-
1
def expression
-
self.class::EXPRESSION
-
end
-
end
-
-
# @private
-
1
class AllowanceTarget < TargetBase
-
1
EXPRESSION = :allow
-
1
delegate_to :setup_allowance
-
1
disallow_negation :not_to
-
1
disallow_negation :to_not
-
end
-
-
# @private
-
1
class ExpectationTarget < TargetBase
-
1
EXPRESSION = :expect
-
1
delegate_to :setup_expectation
-
1
delegate_not_to :setup_negative_expectation, :from => :not_to
-
1
delegate_not_to :setup_negative_expectation, :from => :to_not
-
end
-
-
# @private
-
1
class AnyInstanceAllowanceTarget < TargetBase
-
1
EXPRESSION = :allow_any_instance_of
-
1
delegate_to :setup_any_instance_allowance
-
1
disallow_negation :not_to
-
1
disallow_negation :to_not
-
end
-
-
# @private
-
1
class AnyInstanceExpectationTarget < TargetBase
-
1
EXPRESSION = :expect_any_instance_of
-
1
delegate_to :setup_any_instance_expectation
-
1
delegate_not_to :setup_any_instance_negative_expectation, :from => :not_to
-
1
delegate_not_to :setup_any_instance_negative_expectation, :from => :to_not
-
end
-
end
-
end
-
1
module RSpec
-
1
module Mocks
-
# Implements the methods needed for a pure test double. RSpec::Mocks::Double
-
# includes this module, and it is provided for cases where you want a
-
# pure test double without subclassing RSpec::Mocks::Double.
-
1
module TestDouble
-
# Creates a new test double with a `name` (that will be used in error
-
# messages only)
-
1
def initialize(name=nil, stubs={})
-
@__expired = false
-
if Hash === name && stubs.empty?
-
stubs = name
-
@name = nil
-
else
-
@name = name
-
end
-
assign_stubs(stubs)
-
end
-
-
# Tells the object to respond to all messages. If specific stub values
-
# are declared, they'll work as expected. If not, the receiver is
-
# returned.
-
1
def as_null_object
-
__mock_proxy.as_null_object
-
end
-
-
# Returns true if this object has received `as_null_object`
-
1
def null_object?
-
__mock_proxy.null_object?
-
end
-
-
# This allows for comparing the mock to other objects that proxy such as
-
# ActiveRecords belongs_to proxy objects. By making the other object run
-
# the comparison, we're sure the call gets delegated to the proxy
-
# target.
-
1
def ==(other)
-
other == __mock_proxy
-
end
-
-
# @private
-
1
def inspect
-
TestDoubleFormatter.format(self)
-
end
-
-
# @private
-
1
def to_s
-
inspect.gsub('<', '[').gsub('>', ']')
-
end
-
-
# @private
-
1
def respond_to?(message, incl_private=false)
-
__mock_proxy.null_object? ? true : super
-
end
-
-
# @private
-
1
def __build_mock_proxy_unless_expired(order_group)
-
__raise_expired_error || __build_mock_proxy(order_group)
-
end
-
-
# @private
-
1
def __disallow_further_usage!
-
@__expired = true
-
end
-
-
# Override for default freeze implementation to prevent freezing of test
-
# doubles.
-
1
def freeze
-
RSpec.warn_with("WARNING: you attempted to freeze a test double. This is explicitly a no-op as freezing doubles can lead to undesired behaviour when resetting tests.")
-
end
-
-
1
private
-
-
1
def method_missing(message, *args, &block)
-
proxy = __mock_proxy
-
proxy.record_message_received(message, *args, &block)
-
-
if proxy.null_object?
-
case message
-
when :to_int then return 0
-
when :to_a, :to_ary then return nil
-
when :to_str then return to_s
-
else return self
-
end
-
end
-
-
# Defined private and protected methods will still trigger `method_missing`
-
# when called publicly. We want ruby's method visibility error to get raised,
-
# so we simply delegate to `super` in that case.
-
# ...well, we would delegate to `super`, but there's a JRuby
-
# bug, so we raise our own visibility error instead:
-
# https://github.com/jruby/jruby/issues/1398
-
visibility = proxy.visibility_for(message)
-
if visibility == :private || visibility == :protected
-
ErrorGenerator.new(self).raise_non_public_error(
-
message, visibility
-
)
-
end
-
-
# Required wrapping doubles in an Array on Ruby 1.9.2
-
raise NoMethodError if [:to_a, :to_ary].include? message
-
proxy.raise_unexpected_message_error(message, args)
-
end
-
-
1
def assign_stubs(stubs)
-
stubs.each_pair do |message, response|
-
__mock_proxy.add_simple_stub(message, response)
-
end
-
end
-
-
1
def __mock_proxy
-
::RSpec::Mocks.space.proxy_for(self)
-
end
-
-
1
def __build_mock_proxy(order_group)
-
TestDoubleProxy.new(self, order_group)
-
end
-
-
1
def __raise_expired_error
-
return false unless @__expired
-
ErrorGenerator.new(self).raise_expired_test_double_error
-
end
-
-
1
def initialize_copy(other)
-
as_null_object if other.null_object?
-
super
-
end
-
end
-
-
# A generic test double object. `double`, `instance_double` and friends
-
# return an instance of this.
-
1
class Double
-
1
include TestDouble
-
end
-
-
# @private
-
1
module TestDoubleFormatter
-
1
def self.format(dbl, unwrap=false)
-
format = "#{type_desc(dbl)}#{verified_module_desc(dbl)} #{name_desc(dbl)}"
-
return format if unwrap
-
"#<#{format}>"
-
end
-
-
1
class << self
-
1
private
-
-
1
def type_desc(dbl)
-
case dbl
-
when InstanceVerifyingDouble then "InstanceDouble"
-
when ClassVerifyingDouble then "ClassDouble"
-
when ObjectVerifyingDouble then "ObjectDouble"
-
else "Double"
-
end
-
end
-
-
# @private
-
1
IVAR_GET = Object.instance_method(:instance_variable_get)
-
-
1
def verified_module_desc(dbl)
-
return nil unless VerifyingDouble === dbl
-
"(#{IVAR_GET.bind(dbl).call(:@doubled_module).description})"
-
end
-
-
1
def name_desc(dbl)
-
return "(anonymous)" unless (name = IVAR_GET.bind(dbl).call(:@name))
-
name.inspect
-
end
-
end
-
end
-
end
-
end
-
1
RSpec::Support.require_rspec_mocks 'verifying_proxy'
-
-
1
module RSpec
-
1
module Mocks
-
# @private
-
1
module VerifyingDouble
-
1
def respond_to?(message, include_private=false)
-
return super unless null_object?
-
-
method_ref = __mock_proxy.method_reference[message]
-
-
case method_ref.visibility
-
when :public then true
-
when :private then include_private
-
when :protected then include_private || RUBY_VERSION.to_f < 2.0
-
else !method_ref.unimplemented?
-
end
-
end
-
-
1
def method_missing(message, *args, &block)
-
# Null object conditional is an optimization. If not a null object,
-
# validity of method expectations will have been checked at definition
-
# time.
-
if null_object?
-
if @__sending_message == message
-
__mock_proxy.ensure_implemented(message)
-
else
-
__mock_proxy.ensure_publicly_implemented(message, self)
-
end
-
-
__mock_proxy.validate_arguments!(message, args)
-
end
-
-
super
-
end
-
-
# @private
-
1
module SilentIO
-
1
def self.method_missing(*); end
-
1
def self.respond_to?(*)
-
1
true
-
end
-
end
-
-
# Redefining `__send__` causes ruby to issue a warning.
-
1
old, $stderr = $stderr, SilentIO
-
1
def __send__(name, *args, &block)
-
@__sending_message = name
-
super
-
ensure
-
@__sending_message = nil
-
end
-
1
$stderr = old
-
-
1
def send(name, *args, &block)
-
__send__(name, *args, &block)
-
end
-
-
1
def initialize(doubled_module, *args)
-
@doubled_module = doubled_module
-
-
possible_name = args.first
-
name = if String === possible_name || Symbol === possible_name
-
args.shift
-
end
-
-
super(name, *args)
-
@__sending_message = nil
-
end
-
end
-
-
# A mock providing a custom proxy that can verify the validity of any
-
# method stubs or expectations against the public instance methods of the
-
# given class.
-
#
-
# @private
-
1
class InstanceVerifyingDouble
-
1
include TestDouble
-
1
include VerifyingDouble
-
-
1
def __build_mock_proxy(order_group)
-
VerifyingProxy.new(self, order_group,
-
@doubled_module,
-
InstanceMethodReference
-
)
-
end
-
end
-
-
# An awkward module necessary because we cannot otherwise have
-
# ClassVerifyingDouble inherit from Module and still share these methods.
-
#
-
# @private
-
1
module ObjectVerifyingDoubleMethods
-
1
include TestDouble
-
1
include VerifyingDouble
-
-
1
def as_stubbed_const(options={})
-
ConstantMutator.stub(@doubled_module.const_to_replace, self, options)
-
self
-
end
-
-
1
private
-
-
1
def __build_mock_proxy(order_group)
-
VerifyingProxy.new(self, order_group,
-
@doubled_module,
-
ObjectMethodReference
-
)
-
end
-
end
-
-
# Similar to an InstanceVerifyingDouble, except that it verifies against
-
# public methods of the given object.
-
#
-
# @private
-
1
class ObjectVerifyingDouble
-
1
include ObjectVerifyingDoubleMethods
-
end
-
-
# Effectively the same as an ObjectVerifyingDouble (since a class is a type
-
# of object), except with Module in the inheritance chain so that
-
# transferring nested constants to work.
-
#
-
# @private
-
1
class ClassVerifyingDouble < Module
-
1
include ObjectVerifyingDoubleMethods
-
end
-
end
-
end
-
1
RSpec::Support.require_rspec_support 'method_signature_verifier'
-
-
1
module RSpec
-
1
module Mocks
-
# A message expectation that knows about the real implementation of the
-
# message being expected, so that it can verify that any expectations
-
# have the valid arguments.
-
# @api private
-
1
class VerifyingMessageExpectation < MessageExpectation
-
# A level of indirection is used here rather than just passing in the
-
# method itself, since method look up is expensive and we only want to
-
# do it if actually needed.
-
#
-
# Conceptually the method reference makes more sense as a constructor
-
# argument since it should be immutable, but it is significantly more
-
# straight forward to build the object in pieces so for now it stays as
-
# an accessor.
-
1
attr_accessor :method_reference
-
-
1
def initialize(*args)
-
super
-
end
-
-
# @private
-
1
def with(*args, &block)
-
super(*args, &block).tap do
-
validate_expected_arguments! do |signature|
-
example_call_site_args = [:an_arg] * signature.min_non_kw_args
-
example_call_site_args << :kw_args_hash if signature.required_kw_args.any?
-
@argument_list_matcher.resolve_expected_args_based_on(example_call_site_args)
-
end
-
end
-
end
-
-
1
private
-
-
1
def validate_expected_arguments!
-
return if method_reference.nil?
-
-
method_reference.with_signature do |signature|
-
args = yield signature
-
verifier = Support::LooseSignatureVerifier.new(signature, args)
-
-
unless verifier.valid?
-
# Fail fast is required, otherwise the message expecation will fail
-
# as well ("expected method not called") and clobber this one.
-
@failed_fast = true
-
@error_generator.raise_invalid_arguments_error(verifier)
-
end
-
end
-
end
-
end
-
end
-
end
-
1
RSpec::Support.require_rspec_mocks 'verifying_message_expecation'
-
1
RSpec::Support.require_rspec_mocks 'method_reference'
-
-
1
module RSpec
-
1
module Mocks
-
# @private
-
1
class CallbackInvocationStrategy
-
1
def call(doubled_module)
-
RSpec::Mocks.configuration.verifying_double_callbacks.each do |block|
-
block.call doubled_module
-
end
-
end
-
end
-
-
# @private
-
1
class NoCallbackInvocationStrategy
-
1
def call(_doubled_module)
-
end
-
end
-
-
# @private
-
1
module VerifyingProxyMethods
-
1
def add_stub(method_name, opts={}, &implementation)
-
ensure_implemented(method_name)
-
super
-
end
-
-
1
def add_simple_stub(method_name, *args)
-
ensure_implemented(method_name)
-
super
-
end
-
-
1
def add_message_expectation(method_name, opts={}, &block)
-
ensure_implemented(method_name)
-
super
-
end
-
-
1
def ensure_implemented(method_name)
-
return unless method_reference[method_name].unimplemented?
-
-
@error_generator.raise_unimplemented_error(
-
@doubled_module,
-
method_name,
-
@object
-
)
-
end
-
-
1
def ensure_publicly_implemented(method_name, _object)
-
ensure_implemented(method_name)
-
visibility = method_reference[method_name].visibility
-
-
return if visibility == :public
-
@error_generator.raise_non_public_error(method_name, visibility)
-
end
-
end
-
-
# A verifying proxy mostly acts like a normal proxy, except that it
-
# contains extra logic to try and determine the validity of any expectation
-
# set on it. This includes whether or not methods have been defined and the
-
# validatiy of arguments on method calls.
-
#
-
# In all other ways this behaves like a normal proxy. It only adds the
-
# verification behaviour to specific methods then delegates to the parent
-
# implementation.
-
#
-
# These checks are only activated if the doubled class has already been
-
# loaded, otherwise they are disabled. This allows for testing in
-
# isolation.
-
#
-
# @private
-
1
class VerifyingProxy < TestDoubleProxy
-
1
include VerifyingProxyMethods
-
-
1
def initialize(object, order_group, doubled_module, method_reference_class)
-
super(object, order_group)
-
@object = object
-
@doubled_module = doubled_module
-
@method_reference_class = method_reference_class
-
-
# A custom method double is required to pass through a way to lookup
-
# methods to determine their parameters. This is only relevant if the doubled
-
# class is loaded.
-
@method_doubles = Hash.new do |h, k|
-
h[k] = VerifyingMethodDouble.new(@object, k, self, method_reference[k])
-
end
-
end
-
-
1
def method_reference
-
@method_reference ||= Hash.new do |h, k|
-
h[k] = @method_reference_class.for(@doubled_module, k)
-
end
-
end
-
-
1
def visibility_for(method_name)
-
method_reference[method_name].visibility
-
end
-
-
1
def validate_arguments!(method_name, args)
-
@method_doubles[method_name].validate_arguments!(args)
-
end
-
end
-
-
# @private
-
1
DEFAULT_CALLBACK_INVOCATION_STRATEGY = CallbackInvocationStrategy.new
-
-
# @private
-
1
class VerifyingPartialDoubleProxy < PartialDoubleProxy
-
1
include VerifyingProxyMethods
-
-
1
def initialize(object, expectation_ordering, optional_callback_invocation_strategy=DEFAULT_CALLBACK_INVOCATION_STRATEGY)
-
super(object, expectation_ordering)
-
@doubled_module = DirectObjectReference.new(object)
-
-
# A custom method double is required to pass through a way to lookup
-
# methods to determine their parameters.
-
@method_doubles = Hash.new do |h, k|
-
h[k] = VerifyingExistingMethodDouble.for(object, k, self)
-
end
-
-
optional_callback_invocation_strategy.call(@doubled_module)
-
end
-
-
1
def method_reference
-
@method_doubles
-
end
-
end
-
-
# @private
-
1
class VerifyingPartialClassDoubleProxy < VerifyingPartialDoubleProxy
-
1
include PartialClassDoubleProxyMethods
-
end
-
-
# @private
-
1
class VerifyingMethodDouble < MethodDouble
-
1
def initialize(object, method_name, proxy, method_reference)
-
super(object, method_name, proxy)
-
@method_reference = method_reference
-
end
-
-
1
def message_expectation_class
-
VerifyingMessageExpectation
-
end
-
-
1
def add_expectation(*args, &block)
-
# explict params necessary for 1.8.7 see #626
-
super(*args, &block).tap { |x| x.method_reference = @method_reference }
-
end
-
-
1
def add_stub(*args, &block)
-
# explict params necessary for 1.8.7 see #626
-
super(*args, &block).tap { |x| x.method_reference = @method_reference }
-
end
-
-
1
def proxy_method_invoked(obj, *args, &block)
-
validate_arguments!(args)
-
super
-
end
-
-
1
def validate_arguments!(actual_args)
-
@method_reference.with_signature do |signature|
-
verifier = Support::StrictSignatureVerifier.new(signature, actual_args)
-
raise ArgumentError, verifier.error_message unless verifier.valid?
-
end
-
end
-
end
-
-
# A VerifyingMethodDouble fetches the method to verify against from the
-
# original object, using a MethodReference. This works for pure doubles,
-
# but when the original object is itself the one being modified we need to
-
# collapse the reference and the method double into a single object so that
-
# we can access the original pristine method definition.
-
#
-
# @private
-
1
class VerifyingExistingMethodDouble < VerifyingMethodDouble
-
1
def initialize(object, method_name, proxy)
-
super(object, method_name, proxy, self)
-
-
@valid_method = object.respond_to?(method_name, true)
-
-
# Trigger an eager find of the original method since if we find it any
-
# later we end up getting a stubbed method with incorrect arity.
-
save_original_implementation_callable!
-
end
-
-
1
def with_signature
-
yield Support::MethodSignature.new(original_implementation_callable)
-
end
-
-
1
def unimplemented?
-
!@valid_method
-
end
-
-
1
def self.for(object, method_name, proxy)
-
if ClassNewMethodReference.applies_to?(method_name) { object }
-
VerifyingExistingClassNewMethodDouble
-
else
-
self
-
end.new(object, method_name, proxy)
-
end
-
end
-
-
# Used in place of a `VerifyingExistingMethodDouble` for the specific case
-
# of mocking or stubbing a `new` method on a class. In this case, we substitute
-
# the method signature from `#initialize` since new's signature is just `*args`.
-
#
-
# @private
-
1
class VerifyingExistingClassNewMethodDouble < VerifyingExistingMethodDouble
-
1
def with_signature
-
yield Support::MethodSignature.new(object.instance_method(:initialize))
-
end
-
end
-
end
-
end
-
1
module RSpec
-
1
module Mocks
-
# Version information for RSpec mocks.
-
1
module Version
-
# Version of RSpec mocks currently in use in SemVer format.
-
1
STRING = '3.3.2'
-
end
-
end
-
end
-
1
module RSpec
-
1
module Support
-
# Provides a means to fuzzy-match between two arbitrary objects.
-
# Understands array/hash nesting. Uses `===` or `==` to
-
# perform the matching.
-
1
module FuzzyMatcher
-
# @api private
-
1
def self.values_match?(expected, actual)
-
344
if Hash === actual
-
return hashes_match?(expected, actual) if Hash === expected
-
36
elsif Array === expected && Enumerable === actual && !(Struct === actual)
-
36
return arrays_match?(expected, actual.to_a)
-
end
-
-
308
return true if expected == actual
-
-
258
begin
-
258
expected === actual
-
rescue ArgumentError
-
# Some objects, like 0-arg lambdas on 1.9+, raise
-
# ArgumentError for `expected === actual`.
-
false
-
end
-
end
-
-
# @private
-
1
def self.arrays_match?(expected_list, actual_list)
-
36
return false if expected_list.size != actual_list.size
-
-
36
expected_list.zip(actual_list).all? do |expected, actual|
-
56
values_match?(expected, actual)
-
end
-
end
-
-
# @private
-
1
def self.hashes_match?(expected_hash, actual_hash)
-
return false if expected_hash.size != actual_hash.size
-
-
expected_hash.all? do |expected_key, expected_value|
-
actual_value = actual_hash.fetch(expected_key) { return false }
-
values_match?(expected_value, actual_value)
-
end
-
end
-
-
1
private_class_method :arrays_match?, :hashes_match?
-
end
-
end
-
end
-
1
module RSpec
-
1
module Support
-
# @private
-
1
def self.matcher_definitions
-
2
@matcher_definitions ||= []
-
end
-
-
# Used internally to break cyclic dependency between mocks, expectations,
-
# and support. We don't currently have a consistent implementation of our
-
# matchers, though we are considering changing that:
-
# https://github.com/rspec/rspec-mocks/issues/513
-
#
-
# @private
-
1
def self.register_matcher_definition(&block)
-
2
matcher_definitions << block
-
end
-
-
# Remove a previously registered matcher. Useful for cleaning up after
-
# yourself in specs.
-
#
-
# @private
-
1
def self.deregister_matcher_definition(&block)
-
matcher_definitions.delete(block)
-
end
-
-
# @private
-
1
def self.is_a_matcher?(object)
-
matcher_definitions.any? { |md| md.call(object) }
-
end
-
-
# @api private
-
#
-
# gives a string representation of an object for use in RSpec descriptions
-
1
def self.rspec_description_for_object(object)
-
if RSpec::Support.is_a_matcher?(object) && object.respond_to?(:description)
-
object.description
-
else
-
object
-
end
-
end
-
end
-
end
-
1
require 'rspec/support'
-
1
RSpec::Support.require_rspec_support "ruby_features"
-
1
RSpec::Support.require_rspec_support "matcher_definition"
-
-
1
module RSpec
-
1
module Support
-
# Extracts info about the number of arguments and allowed/required
-
# keyword args of a given method.
-
#
-
# @private
-
1
class MethodSignature
-
1
attr_reader :min_non_kw_args, :max_non_kw_args, :optional_kw_args, :required_kw_args
-
-
1
def initialize(method)
-
@method = method
-
@optional_kw_args = []
-
@required_kw_args = []
-
classify_parameters
-
end
-
-
1
def non_kw_args_arity_description
-
case max_non_kw_args
-
when min_non_kw_args then min_non_kw_args.to_s
-
when INFINITY then "#{min_non_kw_args} or more"
-
else "#{min_non_kw_args} to #{max_non_kw_args}"
-
end
-
end
-
-
1
def valid_non_kw_args?(positional_arg_count)
-
min_non_kw_args <= positional_arg_count &&
-
positional_arg_count <= max_non_kw_args
-
end
-
-
1
if RubyFeatures.optional_and_splat_args_supported?
-
1
def description
-
@description ||= begin
-
parts = []
-
-
unless non_kw_args_arity_description == "0"
-
parts << "arity of #{non_kw_args_arity_description}"
-
end
-
-
if @optional_kw_args.any?
-
parts << "optional keyword args (#{@optional_kw_args.map(&:inspect).join(", ")})"
-
end
-
-
if @required_kw_args.any?
-
parts << "required keyword args (#{@required_kw_args.map(&:inspect).join(", ")})"
-
end
-
-
parts << "any additional keyword args" if @allows_any_kw_args
-
-
parts.join(" and ")
-
end
-
end
-
-
1
def missing_kw_args_from(given_kw_args)
-
@required_kw_args - given_kw_args
-
end
-
-
1
def invalid_kw_args_from(given_kw_args)
-
return [] if @allows_any_kw_args
-
given_kw_args - @allowed_kw_args
-
end
-
-
1
def has_kw_args_in?(args)
-
Hash === args.last && could_contain_kw_args?(args)
-
end
-
-
# Without considering what the last arg is, could it
-
# contain keyword arguments?
-
1
def could_contain_kw_args?(args)
-
return false if args.count <= min_non_kw_args
-
@allows_any_kw_args || @allowed_kw_args.any?
-
end
-
-
1
def classify_parameters
-
optional_non_kw_args = @min_non_kw_args = 0
-
@allows_any_kw_args = false
-
-
@method.parameters.each do |(type, name)|
-
case type
-
# def foo(a:)
-
when :keyreq then @required_kw_args << name
-
# def foo(a: 1)
-
when :key then @optional_kw_args << name
-
# def foo(**kw_args)
-
when :keyrest then @allows_any_kw_args = true
-
# def foo(a)
-
when :req then @min_non_kw_args += 1
-
# def foo(a = 1)
-
when :opt then optional_non_kw_args += 1
-
# def foo(*a)
-
when :rest then optional_non_kw_args = INFINITY
-
end
-
end
-
-
@max_non_kw_args = @min_non_kw_args + optional_non_kw_args
-
@allowed_kw_args = @required_kw_args + @optional_kw_args
-
end
-
else
-
def description
-
"arity of #{non_kw_args_arity_description}"
-
end
-
-
def missing_kw_args_from(_given_kw_args)
-
[]
-
end
-
-
def invalid_kw_args_from(_given_kw_args)
-
[]
-
end
-
-
def has_kw_args_in?(_args)
-
false
-
end
-
-
def could_contain_kw_args?(*)
-
false
-
end
-
-
def classify_parameters
-
arity = @method.arity
-
if arity < 0
-
# `~` inverts the one's complement and gives us the
-
# number of required args
-
@min_non_kw_args = ~arity
-
@max_non_kw_args = INFINITY
-
else
-
@min_non_kw_args = arity
-
@max_non_kw_args = arity
-
end
-
end
-
end
-
-
1
INFINITY = 1 / 0.0
-
end
-
-
# Deals with the slightly different semantics of block arguments.
-
# For methods, arguments are required unless a default value is provided.
-
# For blocks, arguments are optional, even if no default value is provided.
-
#
-
# However, we want to treat block args as required since you virtually
-
# always want to pass a value for each received argument and our
-
# `and_yield` has treated block args as required for many years.
-
#
-
# @api private
-
1
class BlockSignature < MethodSignature
-
1
if RubyFeatures.optional_and_splat_args_supported?
-
1
def classify_parameters
-
super
-
@min_non_kw_args = @max_non_kw_args unless @max_non_kw_args == INFINITY
-
end
-
end
-
end
-
-
# Abstract base class for signature verifiers.
-
#
-
# @api private
-
1
class MethodSignatureVerifier
-
1
attr_reader :non_kw_args, :kw_args
-
-
1
def initialize(signature, args)
-
@signature = signature
-
@non_kw_args, @kw_args = split_args(*args)
-
end
-
-
1
def valid?
-
missing_kw_args.empty? &&
-
invalid_kw_args.empty? &&
-
valid_non_kw_args?
-
end
-
-
1
def error_message
-
if missing_kw_args.any?
-
"Missing required keyword arguments: %s" % [
-
missing_kw_args.join(", ")
-
]
-
elsif invalid_kw_args.any?
-
"Invalid keyword arguments provided: %s" % [
-
invalid_kw_args.join(", ")
-
]
-
elsif !valid_non_kw_args?
-
"Wrong number of arguments. Expected %s, got %s." % [
-
@signature.non_kw_args_arity_description,
-
non_kw_args.length
-
]
-
end
-
end
-
-
1
private
-
-
1
def valid_non_kw_args?
-
@signature.valid_non_kw_args?(non_kw_args.length)
-
end
-
-
1
def missing_kw_args
-
@signature.missing_kw_args_from(kw_args)
-
end
-
-
1
def invalid_kw_args
-
@signature.invalid_kw_args_from(kw_args)
-
end
-
-
1
def split_args(*args)
-
kw_args = if @signature.has_kw_args_in?(args)
-
args.pop.keys
-
else
-
[]
-
end
-
-
[args, kw_args]
-
end
-
end
-
-
# Figures out wether a given method can accept various arguments.
-
# Surprisingly non-trivial.
-
#
-
# @private
-
1
StrictSignatureVerifier = MethodSignatureVerifier
-
-
# Allows matchers to be used instead of providing keyword arguments. In
-
# practice, when this happens only the arity of the method is verified.
-
#
-
# @private
-
1
class LooseSignatureVerifier < MethodSignatureVerifier
-
1
private
-
-
1
def split_args(*args)
-
if RSpec::Support.is_a_matcher?(args.last) && @signature.could_contain_kw_args?(args)
-
args.pop
-
@signature = SignatureWithKeywordArgumentsMatcher.new(@signature)
-
end
-
-
super(*args)
-
end
-
-
# If a matcher is used in a signature in place of keyword arguments, all
-
# keyword argument validation needs to be skipped since the matcher is
-
# opaque.
-
#
-
# Instead, keyword arguments will be validated when the method is called
-
# and they are actually known.
-
#
-
# @private
-
1
class SignatureWithKeywordArgumentsMatcher
-
1
def initialize(signature)
-
@signature = signature
-
end
-
-
1
def missing_kw_args_from(_kw_args)
-
[]
-
end
-
-
1
def invalid_kw_args_from(_kw_args)
-
[]
-
end
-
-
1
def non_kw_args_arity_description
-
@signature.non_kw_args_arity_description
-
end
-
-
1
def valid_non_kw_args?(*args)
-
@signature.valid_non_kw_args?(*args)
-
end
-
-
1
def has_kw_args_in?(args)
-
@signature.has_kw_args_in?(args)
-
end
-
end
-
end
-
end
-
end
-
1
module RSpec
-
1
module Support
-
# Provide additional output details beyond what `inspect` provides when
-
# printing Time, DateTime, or BigDecimal
-
1
module ObjectFormatter
-
# @api private
-
1
def self.format(object)
-
prepare_for_inspection(object).inspect
-
end
-
-
# rubocop:disable MethodLength
-
-
# @private
-
# Prepares the provided object to be formatted by wrapping it as needed
-
# in something that, when `inspect` is called on it, will produce the
-
# desired output.
-
#
-
# This allows us to apply the desired formatting to hash/array data structures
-
# at any level of nesting, simply by walking that structure and replacing items
-
# with custom items that have `inspect` defined to return the desired output
-
# for that item. Then we can just use `Array#inspect` or `Hash#inspect` to
-
# format the entire thing.
-
1
def self.prepare_for_inspection(object)
-
case object
-
when Array
-
return object.map { |o| prepare_for_inspection(o) }
-
when Hash
-
return prepare_hash(object)
-
when Time
-
inspection = format_time(object)
-
else
-
if defined?(DateTime) && DateTime === object
-
inspection = format_date_time(object)
-
elsif defined?(BigDecimal) && BigDecimal === object
-
inspection = "#{object.to_s 'F'} (#{object.inspect})"
-
elsif RSpec::Support.is_a_matcher?(object) && object.respond_to?(:description)
-
inspection = object.description
-
else
-
return object
-
end
-
end
-
-
InspectableItem.new(inspection)
-
end
-
# rubocop:enable MethodLength
-
-
# @private
-
1
def self.prepare_hash(input)
-
input.inject({}) do |hash, (k, v)|
-
hash[prepare_for_inspection(k)] = prepare_for_inspection(v)
-
hash
-
end
-
end
-
-
1
TIME_FORMAT = "%Y-%m-%d %H:%M:%S"
-
-
1
if Time.method_defined?(:nsec)
-
# @private
-
1
def self.format_time(time)
-
time.strftime("#{TIME_FORMAT}.#{"%09d" % time.nsec} %z")
-
end
-
else # for 1.8.7
-
# @private
-
def self.format_time(time)
-
time.strftime("#{TIME_FORMAT}.#{"%06d" % time.usec} %z")
-
end
-
end
-
-
1
DATE_TIME_FORMAT = "%a, %d %b %Y %H:%M:%S.%N %z"
-
# ActiveSupport sometimes overrides inspect. If `ActiveSupport` is
-
# defined use a custom format string that includes more time precision.
-
# @private
-
1
def self.format_date_time(date_time)
-
if defined?(ActiveSupport)
-
date_time.strftime(DATE_TIME_FORMAT)
-
else
-
date_time.inspect
-
end
-
end
-
-
# @private
-
1
InspectableItem = Struct.new(:inspection) do
-
1
def inspect
-
inspection
-
end
-
-
1
def pretty_print(pp)
-
pp.text inspection
-
end
-
end
-
end
-
end
-
end
-
1
require 'slop/option'
-
1
require 'slop/commands'
-
-
1
class Slop
-
1
include Enumerable
-
-
1
VERSION = '3.6.0'
-
-
# The main Error class, all Exception classes inherit from this class.
-
1
class Error < StandardError; end
-
-
# Raised when an option argument is expected but none are given.
-
1
class MissingArgumentError < Error; end
-
-
# Raised when an option is expected/required but not present.
-
1
class MissingOptionError < Error; end
-
-
# Raised when an argument does not match its intended match constraint.
-
1
class InvalidArgumentError < Error; end
-
-
# Raised when an invalid option is found and the strict flag is enabled.
-
1
class InvalidOptionError < Error; end
-
-
# Raised when an invalid command is found and the strict flag is enabled.
-
1
class InvalidCommandError < Error; end
-
-
# Returns a default Hash of configuration options this Slop instance uses.
-
1
DEFAULT_OPTIONS = {
-
:strict => false,
-
:help => false,
-
:banner => nil,
-
:ignore_case => false,
-
:autocreate => false,
-
:arguments => false,
-
:optional_arguments => false,
-
:multiple_switches => true,
-
:longest_flag => 0
-
}
-
-
1
class << self
-
-
# items - The Array of items to extract options from (default: ARGV).
-
# config - The Hash of configuration options to send to Slop.new().
-
# block - An optional block used to add options.
-
#
-
# Examples:
-
#
-
# Slop.parse(ARGV, :help => true) do
-
# on '-n', '--name', 'Your username', :argument => true
-
# end
-
#
-
# Returns a new instance of Slop.
-
1
def parse(items = ARGV, config = {}, &block)
-
parse! items.dup, config, &block
-
end
-
-
# items - The Array of items to extract options from (default: ARGV).
-
# config - The Hash of configuration options to send to Slop.new().
-
# block - An optional block used to add options.
-
#
-
# Returns a new instance of Slop.
-
1
def parse!(items = ARGV, config = {}, &block)
-
config, items = items, ARGV if items.is_a?(Hash) && config.empty?
-
slop = new config, &block
-
slop.parse! items
-
slop
-
end
-
-
# Build a Slop object from a option specification.
-
#
-
# This allows you to design your options via a simple String rather
-
# than programatically. Do note though that with this method, you're
-
# unable to pass any advanced options to the on() method when creating
-
# options.
-
#
-
# string - The optspec String
-
# config - A Hash of configuration options to pass to Slop.new
-
#
-
# Examples:
-
#
-
# opts = Slop.optspec(<<-SPEC)
-
# ruby foo.rb [options]
-
# ---
-
# n,name= Your name
-
# a,age= Your age
-
# A,auth Sign in with auth
-
# p,passcode= Your secret pass code
-
# SPEC
-
#
-
# opts.fetch_option(:name).description #=> "Your name"
-
#
-
# Returns a new instance of Slop.
-
1
def optspec(string, config = {})
-
warn "[DEPRECATED] `Slop.optspec` is deprecated and will be removed in version 4"
-
config[:banner], optspec = string.split(/^--+$/, 2) if string[/^--+$/]
-
lines = optspec.split("\n").reject(&:empty?)
-
opts = Slop.new(config)
-
-
lines.each do |line|
-
opt, description = line.split(' ', 2)
-
short, long = opt.split(',').map { |s| s.sub(/\A--?/, '') }
-
opt = opts.on(short, long, description)
-
-
if long && long.end_with?('=')
-
long.sub!(/\=$/, '')
-
opt.config[:argument] = true
-
end
-
end
-
-
opts
-
end
-
-
end
-
-
# The Hash of configuration options for this Slop instance.
-
1
attr_reader :config
-
-
# The Array of Slop::Option objects tied to this Slop instance.
-
1
attr_reader :options
-
-
# The Hash of sub-commands for this Slop instance.
-
1
attr_reader :commands
-
-
# Create a new instance of Slop and optionally build options via a block.
-
#
-
# config - A Hash of configuration options.
-
# block - An optional block used to specify options.
-
1
def initialize(config = {}, &block)
-
5
@config = DEFAULT_OPTIONS.merge(config)
-
5
@options = []
-
5
@commands = {}
-
5
@trash = []
-
5
@triggered_options = []
-
5
@unknown_options = []
-
5
@callbacks = {}
-
5
@separators = {}
-
5
@runner = nil
-
5
@command = config.delete(:command)
-
-
5
if block_given?
-
5
block.arity == 1 ? yield(self) : instance_eval(&block)
-
end
-
-
5
if config[:help]
-
on('-h', '--help', 'Display this help message.', :tail => true) do
-
puts help
-
exit
-
end
-
end
-
end
-
-
# Is strict mode enabled?
-
#
-
# Returns true if strict mode is enabled, false otherwise.
-
1
def strict?
-
6
config[:strict]
-
end
-
-
# Set the banner.
-
#
-
# banner - The String to set the banner.
-
1
def banner=(banner)
-
config[:banner] = banner
-
end
-
-
# Get or set the banner.
-
#
-
# banner - The String to set the banner.
-
#
-
# Returns the banner String.
-
1
def banner(banner = nil)
-
4
config[:banner] = banner if banner
-
4
config[:banner]
-
end
-
-
# Set the description (used for commands).
-
#
-
# desc - The String to set the description.
-
1
def description=(desc)
-
config[:description] = desc
-
end
-
-
# Get or set the description (used for commands).
-
#
-
# desc - The String to set the description.
-
#
-
# Returns the description String.
-
1
def description(desc = nil)
-
config[:description] = desc if desc
-
config[:description]
-
end
-
-
# Add a new command.
-
#
-
# command - The Symbol or String used to identify this command.
-
# options - A Hash of configuration options (see Slop::new)
-
#
-
# Returns a new instance of Slop mapped to this command.
-
1
def command(command, options = {}, &block)
-
options = @config.merge(options)
-
@commands[command.to_s] = Slop.new(options.merge(:command => command.to_s), &block)
-
end
-
-
# Parse a list of items, executing and gathering options along the way.
-
#
-
# items - The Array of items to extract options from (default: ARGV).
-
# block - An optional block which when used will yield non options.
-
#
-
# Returns an Array of original items.
-
1
def parse(items = ARGV, &block)
-
1
parse! items.dup, &block
-
1
items
-
end
-
-
# Parse a list of items, executing and gathering options along the way.
-
# unlike parse() this method will remove any options and option arguments
-
# from the original Array.
-
#
-
# items - The Array of items to extract options from (default: ARGV).
-
# block - An optional block which when used will yield non options.
-
#
-
# Returns an Array of original items with options removed.
-
1
def parse!(items = ARGV, &block)
-
5
if items.empty? && @callbacks[:empty]
-
@callbacks[:empty].each { |cb| cb.call(self) }
-
return items
-
end
-
-
# reset the trash so it doesn't carry over if you parse multiple
-
# times with the same instance
-
5
@trash.clear
-
-
5
if cmd = @commands[items[0]]
-
items.shift
-
return cmd.parse! items
-
end
-
-
5
items.each_with_index do |item, index|
-
10
@trash << index && break if item == '--'
-
10
autocreate(items, index) if config[:autocreate]
-
10
process_item(items, index, &block) unless @trash.include?(index)
-
end
-
15
items.reject!.with_index { |item, index| @trash.include?(index) }
-
-
30
missing_options = options.select { |opt| opt.required? && opt.count < 1 }
-
5
if missing_options.any?
-
raise MissingOptionError,
-
"Missing required option(s): #{missing_options.map(&:key).join(', ')}"
-
end
-
-
5
if @unknown_options.any?
-
raise InvalidOptionError, "Unknown options #{@unknown_options.join(', ')}"
-
end
-
-
5
if @triggered_options.empty? && @callbacks[:no_options]
-
@callbacks[:no_options].each { |cb| cb.call(self) }
-
end
-
-
5
if @runner.respond_to?(:call)
-
@runner.call(self, items) unless config[:help] and present?(:help)
-
end
-
-
5
items
-
end
-
-
# Add an Option.
-
#
-
# objects - An Array with an optional Hash as the last element.
-
#
-
# Examples:
-
#
-
# on '-u', '--username=', 'Your username'
-
# on :v, :verbose, 'Enable verbose mode'
-
#
-
# Returns the created instance of Slop::Option.
-
1
def on(*objects, &block)
-
25
option = build_option(objects, &block)
-
25
original = options.find do |o|
-
60
o.long and o.long == option.long or o.short and o.short == option.short
-
end
-
25
options.delete(original) if original
-
25
options << option
-
25
option
-
end
-
1
alias option on
-
1
alias opt on
-
-
# Fetch an options argument value.
-
#
-
# key - The Symbol or String option short or long flag.
-
#
-
# Returns the Object value for this option, or nil.
-
1
def [](key)
-
option = fetch_option(key)
-
option.value if option
-
end
-
1
alias get []
-
-
# Returns a new Hash with option flags as keys and option values as values.
-
#
-
# include_commands - If true, merge options from all sub-commands.
-
1
def to_hash(include_commands = false)
-
hash = Hash[options.map { |opt| [opt.key.to_sym, opt.value] }]
-
if include_commands
-
@commands.each { |cmd, opts| hash.merge!(cmd.to_sym => opts.to_hash) }
-
end
-
hash
-
end
-
1
alias to_h to_hash
-
-
# Enumerable interface. Yields each Slop::Option.
-
1
def each(&block)
-
options.each(&block)
-
end
-
-
# Specify code to be executed when these options are parsed.
-
#
-
# callable - An object responding to a call method.
-
#
-
# yields - The instance of Slop parsing these options
-
# An Array of unparsed arguments
-
#
-
# Example:
-
#
-
# Slop.parse do
-
# on :v, :verbose
-
#
-
# run do |opts, args|
-
# puts "Arguments: #{args.inspect}" if opts.verbose?
-
# end
-
# end
-
1
def run(callable = nil, &block)
-
@runner = callable || block
-
unless @runner.respond_to?(:call)
-
raise ArgumentError, "You must specify a callable object or a block to #run"
-
end
-
end
-
-
# Check for an options presence.
-
#
-
# Examples:
-
#
-
# opts.parse %w( --foo )
-
# opts.present?(:foo) #=> true
-
# opts.present?(:bar) #=> false
-
#
-
# Returns true if all of the keys are present in the parsed arguments.
-
1
def present?(*keys)
-
80
keys.all? { |key| (opt = fetch_option(key)) && opt.count > 0 }
-
end
-
-
# Override this method so we can check if an option? method exists.
-
#
-
# Returns true if this option key exists in our list of options.
-
1
def respond_to_missing?(method_name, include_private = false)
-
options.any? { |o| o.key == method_name.to_s.chop } || super
-
end
-
-
# Fetch a list of options which were missing from the parsed list.
-
#
-
# Examples:
-
#
-
# opts = Slop.new do
-
# on :n, :name=
-
# on :p, :password=
-
# end
-
#
-
# opts.parse %w[ --name Lee ]
-
# opts.missing #=> ['password']
-
#
-
# Returns an Array of Strings representing missing options.
-
1
def missing
-
(options - @triggered_options).map(&:key)
-
end
-
-
# Fetch a Slop::Option object.
-
#
-
# key - The Symbol or String option key.
-
#
-
# Examples:
-
#
-
# opts.on(:foo, 'Something fooey', :argument => :optional)
-
# opt = opts.fetch_option(:foo)
-
# opt.class #=> Slop::Option
-
# opt.accepts_optional_argument? #=> true
-
#
-
# Returns an Option or nil if none were found.
-
1
def fetch_option(key)
-
200
options.find { |option| [option.long, option.short].include?(clean(key)) }
-
end
-
-
# Fetch a Slop object associated with this command.
-
#
-
# command - The String or Symbol name of the command.
-
#
-
# Examples:
-
#
-
# opts.command :foo do
-
# on :v, :verbose, 'Enable verbose mode'
-
# end
-
#
-
# # ruby run.rb foo -v
-
# opts.fetch_command(:foo).verbose? #=> true
-
1
def fetch_command(command)
-
@commands[command.to_s]
-
end
-
-
# Add a callback.
-
#
-
# label - The Symbol identifier to attach this callback.
-
#
-
# Returns nothing.
-
1
def add_callback(label, &block)
-
(@callbacks[label] ||= []) << block
-
end
-
-
# Add string separators between options.
-
#
-
# text - The String text to print.
-
1
def separator(text)
-
if @separators[options.size]
-
@separators[options.size] << "\n#{text}"
-
else
-
@separators[options.size] = text
-
end
-
end
-
-
# Print a handy Slop help string.
-
#
-
# Returns the banner followed by available option help strings.
-
1
def to_s
-
heads = options.reject(&:tail?)
-
tails = (options - heads)
-
opts = (heads + tails).select(&:help).map(&:to_s)
-
optstr = opts.each_with_index.map { |o, i|
-
(str = @separators[i + 1]) ? [o, str].join("\n") : o
-
}.join("\n")
-
-
if @commands.any?
-
optstr << "\n" if !optstr.empty?
-
optstr << "\nAvailable commands:\n\n"
-
optstr << commands_to_help
-
optstr << "\n\nSee `<command> --help` for more information on a specific command."
-
end
-
-
banner = config[:banner]
-
if banner.nil?
-
banner = "Usage: #{File.basename($0, '.*')}"
-
banner << " #{@command}" if @command
-
banner << " [command]" if @commands.any?
-
banner << " [options]"
-
end
-
if banner
-
"#{banner}\n#{@separators[0] ? "#{@separators[0]}\n" : ''}#{optstr}"
-
else
-
optstr
-
end
-
end
-
1
alias help to_s
-
-
1
private
-
-
# Convenience method for present?(:option).
-
#
-
# Examples:
-
#
-
# opts.parse %( --verbose )
-
# opts.verbose? #=> true
-
# opts.other? #=> false
-
#
-
# Returns true if this option is present. If this method does not end
-
# with a ? character it will instead call super().
-
1
def method_missing(method, *args, &block)
-
4
meth = method.to_s
-
4
if meth.end_with?('?')
-
4
meth.chop!
-
4
present?(meth) || present?(meth.gsub('_', '-'))
-
else
-
super
-
end
-
end
-
-
# Process a list item, figure out if it's an option, execute any
-
# callbacks, assign any option arguments, and do some sanity checks.
-
#
-
# items - The Array of items to process.
-
# index - The current Integer index of the item we want to process.
-
# block - An optional block which when passed will yield non options.
-
#
-
# Returns nothing.
-
1
def process_item(items, index, &block)
-
10
return unless item = items[index]
-
10
option, argument = extract_option(item) if item.start_with?('-')
-
-
10
if option
-
4
option.count += 1 unless item.start_with?('--no-')
-
4
option.count += 1 if option.key[0, 3] == "no-"
-
4
@trash << index
-
4
@triggered_options << option
-
-
4
if option.expects_argument?
-
argument ||= items.at(index + 1)
-
-
if !argument || argument =~ /\A--?[a-zA-Z][a-zA-Z0-9_-]*\z/
-
raise MissingArgumentError, "#{option.key} expects an argument"
-
end
-
-
execute_option(option, argument, index, item)
-
4
elsif option.accepts_optional_argument?
-
argument ||= items.at(index + 1)
-
-
if argument && argument =~ /\A([^\-?]|-\d)+/
-
execute_option(option, argument, index, item)
-
else
-
option.call(nil)
-
end
-
4
elsif config[:multiple_switches] && argument
-
execute_multiple_switches(option, argument, items, index)
-
else
-
4
option.value = option.count > 0
-
4
option.call(nil)
-
end
-
else
-
6
@unknown_options << item if strict? && item =~ /\A--?/
-
6
block.call(item) if block && !@trash.include?(index)
-
end
-
end
-
-
# Execute an option, firing off callbacks and assigning arguments.
-
#
-
# option - The Slop::Option object found by #process_item.
-
# argument - The argument Object to assign to this option.
-
# index - The current Integer index of the object we're processing.
-
# item - The optional String item we're processing.
-
#
-
# Returns nothing.
-
1
def execute_option(option, argument, index, item = nil)
-
if !option
-
if config[:multiple_switches] && strict?
-
raise InvalidOptionError, "Unknown option -#{item}"
-
end
-
return
-
end
-
-
if argument
-
unless item && item.end_with?("=#{argument}")
-
@trash << index + 1 unless option.argument_in_value
-
end
-
option.value = argument
-
else
-
option.value = option.count > 0
-
end
-
-
if option.match? && !argument.match(option.config[:match])
-
raise InvalidArgumentError, "#{argument} is an invalid argument"
-
end
-
-
option.call(option.value)
-
end
-
-
# Execute a `-abc` type option where a, b and c are all options. This
-
# method is only executed if the multiple_switches argument is true.
-
#
-
# option - The first Option object.
-
# argument - The argument to this option. (Split into multiple Options).
-
# items - The Array of items currently being parsed.
-
# index - The index of the current item being processed.
-
#
-
# Returns nothing.
-
1
def execute_multiple_switches(option, argument, items, index)
-
execute_option(option, nil, index)
-
flags = argument.split('')
-
flags.each do |key|
-
if opt = fetch_option(key)
-
opt.count += 1
-
if (opt.expects_argument? || opt.accepts_optional_argument?) &&
-
(flags[-1] == opt.key) && (val = items[index+1])
-
execute_option(opt, val, index, key)
-
else
-
execute_option(opt, nil, index, key)
-
end
-
else
-
raise InvalidOptionError, "Unknown option -#{key}" if strict?
-
end
-
end
-
end
-
-
# Extract an option from a flag.
-
#
-
# flag - The flag key used to extract an option.
-
#
-
# Returns an Array of [option, argument].
-
1
def extract_option(flag)
-
7
option = fetch_option(flag)
-
7
option ||= fetch_option(flag.downcase) if config[:ignore_case]
-
7
option ||= fetch_option(flag.gsub(/([^-])-/, '\1_'))
-
-
7
unless option
-
3
case flag
-
when /\A--?([^=]+)=(.+)\z/, /\A-([a-zA-Z])(.+)\z/, /\A--no-(.+)\z/
-
option, argument = fetch_option($1), ($2 || false)
-
option.argument_in_value = true if option
-
end
-
end
-
-
7
[option, argument]
-
end
-
-
# Autocreate an option on the fly. See the :autocreate Slop config option.
-
#
-
# items - The Array of items we're parsing.
-
# index - The current Integer index for the item we're processing.
-
#
-
# Returns nothing.
-
1
def autocreate(items, index)
-
flag = items[index]
-
if !fetch_option(flag) && !@trash.include?(index)
-
option = build_option(Array(flag))
-
argument = items[index + 1]
-
option.config[:argument] = (argument && argument !~ /\A--?/)
-
option.config[:autocreated] = true
-
options << option
-
end
-
end
-
-
# Build an option from a list of objects.
-
#
-
# objects - An Array of objects used to build this option.
-
#
-
# Returns a new instance of Slop::Option.
-
1
def build_option(objects, &block)
-
25
config = {}
-
25
config[:argument] = true if @config[:arguments]
-
25
config[:optional_argument] = true if @config[:optional_arguments]
-
-
25
if objects.last.is_a?(Hash)
-
config.merge!(objects.pop)
-
end
-
-
25
short = extract_short_flag(objects, config)
-
25
long = extract_long_flag(objects, config)
-
25
desc = objects.shift if objects[0].respond_to?(:to_str)
-
-
25
Option.new(self, short, long, desc, config, &block)
-
end
-
-
1
def extract_short_flag(objects, config)
-
25
flag = objects[0].to_s
-
25
if flag =~ /\A-?\w=?\z/
-
24
config[:argument] ||= flag.end_with?('=')
-
24
objects.shift
-
24
flag.delete('-=')
-
end
-
end
-
-
# Extract the long flag from an item.
-
#
-
# objects - The Array of objects passed from #build_option.
-
# config - The Hash of configuration options built in #build_option.
-
1
def extract_long_flag(objects, config)
-
25
flag = objects.first.to_s
-
25
if flag =~ /\A(?:--?)?[a-zA-Z0-9][a-zA-Z0-9_.-]+\=?\??\z/
-
25
config[:argument] ||= true if flag.end_with?('=')
-
25
config[:optional_argument] = true if flag.end_with?('=?')
-
25
objects.shift
-
25
clean(flag).sub(/\=\??\z/, '')
-
end
-
end
-
-
# Remove any leading -- characters from a string.
-
#
-
# object - The Object we want to cast to a String and clean.
-
#
-
# Returns the newly cleaned String with leading -- characters removed.
-
1
def clean(object)
-
175
object.to_s.sub(/\A--?/, '')
-
end
-
-
1
def commands_to_help
-
padding = 0
-
@commands.each { |c, _| padding = c.size if c.size > padding }
-
@commands.map do |cmd, opts|
-
" #{cmd}#{' ' * (padding - cmd.size)} #{opts.description}"
-
end.join("\n")
-
end
-
-
end
-
1
class Slop
-
1
class Commands
-
1
include Enumerable
-
-
1
attr_reader :config, :commands, :arguments
-
1
attr_writer :banner
-
-
# Create a new instance of Slop::Commands and optionally build
-
# Slop instances via a block. Any configuration options used in
-
# this method will be the default configuration options sent to
-
# each Slop object created.
-
#
-
# config - An optional configuration Hash.
-
# block - Optional block used to define commands.
-
#
-
# Examples:
-
#
-
# commands = Slop::Commands.new do
-
# on :new do
-
# on '-o', '--outdir=', 'The output directory'
-
# on '-v', '--verbose', 'Enable verbose mode'
-
# end
-
#
-
# on :generate do
-
# on '--assets', 'Generate assets', :default => true
-
# end
-
#
-
# global do
-
# on '-D', '--debug', 'Enable debug mode', :default => false
-
# end
-
# end
-
#
-
# commands[:new].class #=> Slop
-
# commands.parse
-
#
-
1
def initialize(config = {}, &block)
-
@config = config
-
@commands = {}
-
@banner = nil
-
@triggered_command = nil
-
-
warn "[DEPRECATED] Slop::Commands is deprecated and will be removed in "\
-
"Slop version 4. Check out http://leejarvis.github.io/slop/#commands for "\
-
"a new implementation of commands."
-
-
if block_given?
-
block.arity == 1 ? yield(self) : instance_eval(&block)
-
end
-
end
-
-
# Optionally set the banner for this command help output.
-
#
-
# banner - The String text to set the banner.
-
#
-
# Returns the String banner if one is set.
-
1
def banner(banner = nil)
-
@banner = banner if banner
-
@banner
-
end
-
-
# Add a Slop instance for a specific command.
-
#
-
# command - A String or Symbol key used to identify this command.
-
# config - A Hash of configuration options to pass to Slop.
-
# block - An optional block used to pass options to Slop.
-
#
-
# Returns the newly created Slop instance mapped to command.
-
1
def on(command, config = {}, &block)
-
commands[command.to_s] = Slop.new(@config.merge(config), &block)
-
end
-
-
# Add a Slop instance used when no other commands exist.
-
#
-
# config - A Hash of configuration options to pass to Slop.
-
# block - An optional block used to pass options to Slop.
-
#
-
# Returns the newly created Slop instance mapped to default.
-
1
def default(config = {}, &block)
-
on('default', config, &block)
-
end
-
-
# Add a global Slop instance.
-
#
-
# config - A Hash of configuration options to pass to Slop.
-
# block - An optional block used to pass options to Slop.
-
#
-
# Returns the newly created Slop instance mapped to global.
-
1
def global(config = {}, &block)
-
on('global', config, &block)
-
end
-
-
# Fetch the instance of Slop tied to a command.
-
#
-
# key - The String or Symbol key used to locate this command.
-
#
-
# Returns the Slop instance if this key is found, nil otherwise.
-
1
def [](key)
-
commands[key.to_s]
-
end
-
1
alias get []
-
-
# Check for a command presence.
-
#
-
# Examples:
-
#
-
# cmds.parse %w( foo )
-
# cmds.present?(:foo) #=> true
-
# cmds.present?(:bar) #=> false
-
#
-
# Returns true if the given key is present in the parsed arguments.
-
1
def present?(key)
-
key.to_s == @triggered_command
-
end
-
-
# Enumerable interface.
-
1
def each(&block)
-
@commands.each(&block)
-
end
-
-
# Parse a list of items.
-
#
-
# items - The Array of items to parse.
-
#
-
# Returns the original Array of items.
-
1
def parse(items = ARGV)
-
parse! items.dup
-
items
-
end
-
-
# Parse a list of items, removing any options or option arguments found.
-
#
-
# items - The Array of items to parse.
-
#
-
# Returns the original Array of items with options removed.
-
1
def parse!(items = ARGV)
-
if opts = commands[items[0].to_s]
-
@triggered_command = items.shift
-
execute_arguments! items
-
opts.parse! items
-
execute_global_opts! items
-
else
-
if opts = commands['default']
-
opts.parse! items
-
else
-
if config[:strict] && items[0]
-
raise InvalidCommandError, "Unknown command `#{items[0]}`"
-
end
-
end
-
execute_global_opts! items
-
end
-
items
-
end
-
-
# Returns a nested Hash with Slop options and values. See Slop#to_hash.
-
1
def to_hash
-
Hash[commands.map { |k, v| [k.to_sym, v.to_hash] }]
-
end
-
-
# Returns the help String.
-
1
def to_s
-
defaults = commands.delete('default')
-
globals = commands.delete('global')
-
helps = commands.reject { |_, v| v.options.none? }
-
if globals && globals.options.any?
-
helps.merge!('Global options' => globals.to_s)
-
end
-
if defaults && defaults.options.any?
-
helps.merge!('Other options' => defaults.to_s)
-
end
-
banner = @banner ? "#{@banner}\n" : ""
-
banner + helps.map { |key, opts| " #{key}\n#{opts}" }.join("\n\n")
-
end
-
1
alias help to_s
-
-
# Returns the inspection String.
-
1
def inspect
-
"#<Slop::Commands #{config.inspect} #{commands.values.map(&:inspect)}>"
-
end
-
-
1
private
-
-
# Returns nothing.
-
1
def execute_arguments!(items)
-
@arguments = items.take_while { |arg| !arg.start_with?('-') }
-
items.shift @arguments.size
-
end
-
-
# Returns nothing.
-
1
def execute_global_opts!(items)
-
if global_opts = commands['global']
-
global_opts.parse! items
-
end
-
end
-
-
end
-
end
-
1
class Slop
-
1
class Option
-
-
# The default Hash of configuration options this class uses.
-
1
DEFAULT_OPTIONS = {
-
:argument => false,
-
:optional_argument => false,
-
:tail => false,
-
:default => nil,
-
:callback => nil,
-
:delimiter => ',',
-
:limit => 0,
-
:match => nil,
-
:optional => true,
-
:required => false,
-
:as => String,
-
:autocreated => false
-
}
-
-
1
attr_reader :short, :long, :description, :config, :types
-
1
attr_accessor :count, :argument_in_value
-
-
# Incapsulate internal option information, mainly used to store
-
# option specific configuration data, most of the meat of this
-
# class is found in the #value method.
-
#
-
# slop - The instance of Slop tied to this Option.
-
# short - The String or Symbol short flag.
-
# long - The String or Symbol long flag.
-
# description - The String description text.
-
# config - A Hash of configuration options.
-
# block - An optional block used as a callback.
-
1
def initialize(slop, short, long, description, config = {}, &block)
-
25
@slop = slop
-
25
@short = short
-
25
@long = long
-
25
@description = description
-
25
@config = DEFAULT_OPTIONS.merge(config)
-
25
@count = 0
-
25
@callback = block_given? ? block : config[:callback]
-
25
@value = nil
-
-
25
@types = {
-
:string => proc { |v| v.to_s },
-
:symbol => proc { |v| v.to_sym },
-
:integer => proc { |v| value_to_integer(v) },
-
:float => proc { |v| value_to_float(v) },
-
:range => proc { |v| value_to_range(v) },
-
:regexp => proc { |v| Regexp.new(v) },
-
:count => proc { |v| @count }
-
}
-
-
25
if long && long.size > @slop.config[:longest_flag]
-
9
@slop.config[:longest_flag] = long.size
-
end
-
-
25
@config.each_key do |key|
-
300
predicate = :"#{key}?"
-
300
unless self.class.method_defined? predicate
-
37
self.class.__send__(:define_method, predicate) { !!@config[key] }
-
end
-
end
-
end
-
-
# Returns true if this option expects an argument.
-
1
def expects_argument?
-
4
config[:argument] && config[:argument] != :optional
-
end
-
-
# Returns true if this option accepts an optional argument.
-
1
def accepts_optional_argument?
-
4
config[:optional_argument] || config[:argument] == :optional
-
end
-
-
# Returns the String flag of this option. Preferring the long flag.
-
1
def key
-
4
long || short
-
end
-
-
# Call this options callback if one exists, and it responds to call().
-
#
-
# Returns nothing.
-
1
def call(*objects)
-
4
@callback.call(*objects) if @callback.respond_to?(:call)
-
end
-
-
# Set the new argument value for this option.
-
#
-
# We use this setter method to handle concatenating lists. That is,
-
# when an array type is specified and used more than once, values from
-
# both options will be grouped together and flattened into a single array.
-
1
def value=(new_value)
-
4
if config[:as].to_s.downcase == 'array'
-
@value ||= []
-
-
if new_value.respond_to?(:split)
-
@value.concat new_value.split(config[:delimiter], config[:limit])
-
end
-
else
-
4
@value = new_value
-
end
-
end
-
-
# Fetch the argument value for this option.
-
#
-
# Returns the Object once any type conversions have taken place.
-
1
def value
-
value = @value.nil? ? config[:default] : @value
-
-
if [true, false, nil].include?(value) && config[:as].to_s != 'count'
-
return value
-
end
-
-
type = config[:as]
-
if type.respond_to?(:call)
-
type.call(value)
-
else
-
if callable = types[type.to_s.downcase.to_sym]
-
callable.call(value)
-
else
-
value
-
end
-
end
-
end
-
-
# Returns the help String for this option.
-
1
def to_s
-
return config[:help] if config[:help].respond_to?(:to_str)
-
-
out = " #{short ? "-#{short}, " : ' ' * 4}"
-
-
if long
-
out << "--#{long}"
-
size = long.size
-
diff = @slop.config[:longest_flag] - size
-
out << (' ' * (diff + 6))
-
else
-
out << (' ' * (@slop.config[:longest_flag] + 8))
-
end
-
-
if config[:default]
-
default = config[:default]
-
"#{out}#{description} (default: #{default})"
-
else
-
"#{out}#{description}"
-
end
-
end
-
1
alias help to_s
-
-
# Returns the String inspection text.
-
1
def inspect
-
"#<Slop::Option [-#{short} | --#{long}" +
-
"#{'=' if expects_argument?}#{'=?' if accepts_optional_argument?}]" +
-
" (#{description}) #{config.inspect}"
-
end
-
-
1
private
-
-
# Convert an object to an Integer if possible.
-
#
-
# value - The Object we want to convert to an integer.
-
#
-
# Returns the Integer value if possible to convert, else a zero.
-
1
def value_to_integer(value)
-
if @slop.strict?
-
begin
-
Integer(value.to_s, 10)
-
rescue ArgumentError
-
raise InvalidArgumentError, "#{value} could not be coerced into Integer"
-
end
-
else
-
value.to_s.to_i
-
end
-
end
-
-
# Convert an object to a Float if possible.
-
#
-
# value - The Object we want to convert to a float.
-
#
-
# Returns the Float value if possible to convert, else a zero.
-
1
def value_to_float(value)
-
if @slop.strict?
-
begin
-
Float(value.to_s)
-
rescue ArgumentError
-
raise InvalidArgumentError, "#{value} could not be coerced into Float"
-
end
-
else
-
value.to_s.to_f
-
end
-
end
-
-
# Convert an object to a Range if possible.
-
#
-
# value - The Object we want to convert to a range.
-
#
-
# Returns the Range value if one could be found, else the original object.
-
1
def value_to_range(value)
-
case value.to_s
-
when /\A(\-?\d+)\z/
-
Range.new($1.to_i, $1.to_i)
-
when /\A(-?\d+?)(\.\.\.?|-|,)(-?\d+)\z/
-
Range.new($1.to_i, $3.to_i, $2 == '...')
-
else
-
if @slop.strict?
-
raise InvalidArgumentError, "#{value} could not be coerced into Range"
-
else
-
value
-
end
-
end
-
end
-
-
end
-
end
-
#Ann John Github
-
1
require 'pry'
-
-
1
WIN_COMBINATIONS= [[0,1,2],[3,4,5],[6,7,8],[0,3,6],[1,4,7],[2,5,8],[0,4,8],[6,4,2]]
-
-
1
def display_board(board)
-
3
puts " #{board[0]} | #{board[1]} | #{board[2]} "
-
3
puts "-----------"
-
3
puts " #{board[3]} | #{board[4]} | #{board[5]} "
-
3
puts "-----------"
-
3
puts " #{board[6]} | #{board[7]} | #{board[8]} "
-
3
binding.pry
-
end
-
-
1
def move(board,position,char="X")
-
board[position.to_i-1]= char
-
end
-
-
1
def position_taken?(board, location)
-
!(board[location].nil? || board[location] == " ")
-
end
-
-
1
def valid_move?(board,position)
-
if (position.to_i-1).between?(0,8) && position_taken?(board,position.to_i-1) == false
-
true
-
elsif position_taken?(board,position.to_i-1) == true
-
false
-
else
-
false
-
end
-
end
-
-
-
1
def turn(board)
-
puts "Please enter 1-9:"
-
position= gets.chomp
-
if valid_move?(board, position) == true
-
move(board,position,current_player(board))
-
else
-
turn(board)
-
end
-
puts display_board(board)
-
end
-
-
1
def turn_count(board)
-
counter = 0
-
board.each do |cell|
-
if cell == "X" || cell == "O"
-
counter += 1
-
end
-
end
-
counter
-
end
-
-
1
def current_player(board)
-
if turn_count(board).to_i.even?
-
"X"
-
else
-
"O"
-
end
-
end
-
-
1
def won?(board)
-
WIN_COMBINATIONS.detect do |position|
-
if board[position[0]] == "X" && board[position[1]] == "X" && board[position[2]] == "X"
-
position
-
elsif board[position[0]] == "O" && board[position[1]] == "O" && board[position[2]] == "O"
-
position
-
else
-
false
-
end
-
end
-
end
-
-
-
1
def full?(board)
-
board.all? do |cell|
-
cell == "X" || cell == "O"
-
end
-
end
-
-
1
def draw?(board)
-
if !won?(board) && full?(board)
-
true
-
elsif !won?(board) && !full?(board)
-
false
-
else won?(board)
-
false
-
end
-
end
-
-
1
def over?(board)
-
if won?(board) || full?(board) || draw?(board)
-
true
-
else
-
false
-
end
-
end
-
-
1
def winner(board)
-
if solution = won?(board)
-
board[solution.first]
-
else
-
nil
-
end
-
end
-
-
1
def play(board)
-
while over?(board) == false
-
turn(board)
-
end
-
if won?(board)
-
puts "Congratulations #{winner(board)}!"
-
elsif draw?(board)
-
puts "Cats Game!"
-
end
-
end
-
-
-
-
-